300 lines
10 KiB
Go
300 lines
10 KiB
Go
package plan
|
|
|
|
import (
|
|
"archive/zip"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type DownloaderTestSuite struct {
|
|
suite.Suite
|
|
|
|
// Home is a temporary directory for use in these tests.
|
|
// It will have a src/ for things to download.
|
|
Home string
|
|
}
|
|
|
|
func (s *DownloaderTestSuite) SetupTest() {
|
|
s.Home = s.T().TempDir()
|
|
s.Assert().NoError(os.MkdirAll(filepath.Join(s.Home, "src"), 0777), "creating src/ dir")
|
|
s.T().Logf("Home: [%s]", s.Home)
|
|
}
|
|
|
|
func TestDownloaderTestSuite(t *testing.T) {
|
|
suite.Run(t, new(DownloaderTestSuite))
|
|
}
|
|
|
|
// TestFile represents a file that will be used for a test.
|
|
type TestFile struct {
|
|
// Name is the relative path and name of the file.
|
|
Name string
|
|
// Contents is the contents of the file.
|
|
Contents []byte
|
|
}
|
|
|
|
func NewTestFile(name, contents string) *TestFile {
|
|
return &TestFile{
|
|
Name: name,
|
|
Contents: []byte(contents),
|
|
}
|
|
}
|
|
|
|
// SaveIn saves this TestFile in the given path.
|
|
// The full path to the file is returned.
|
|
func (f TestFile) SaveIn(path string) (string, error) {
|
|
name := filepath.Join(path, f.Name)
|
|
file, err := os.Create(name)
|
|
if err != nil {
|
|
return name, err
|
|
}
|
|
defer file.Close()
|
|
_, err = file.Write(f.Contents)
|
|
return name, err
|
|
}
|
|
|
|
// TestZip represents a collection of TestFile objects to be zipped into an archive.
|
|
type TestZip []*TestFile
|
|
|
|
func NewTestZip(testFiles ...*TestFile) TestZip {
|
|
tz := make([]*TestFile, len(testFiles))
|
|
for i, tf := range testFiles {
|
|
tz[i] = tf
|
|
}
|
|
return tz
|
|
}
|
|
|
|
// SaveAs saves this TestZip at the given path.
|
|
func (z TestZip) SaveAs(path string) error {
|
|
archive, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer archive.Close()
|
|
zipper := zip.NewWriter(archive)
|
|
for _, tf := range z {
|
|
zfw, zfwerr := zipper.Create(tf.Name)
|
|
if zfwerr != nil {
|
|
return zfwerr
|
|
}
|
|
_, err = zfw.Write(tf.Contents)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return zipper.Close()
|
|
}
|
|
|
|
// saveTestZip saves a TestZip in this test's Home/src directory with the given name.
|
|
// The full path to the saved archive is returned.
|
|
func (s DownloaderTestSuite) saveSrcTestZip(name string, z TestZip) string {
|
|
fullName := filepath.Join(s.Home, "src", name)
|
|
s.Require().NoError(z.SaveAs(fullName), "saving test zip %s", name)
|
|
return fullName
|
|
}
|
|
|
|
// saveSrcTestFile saves a TestFile in this test's Home/src directory.
|
|
// The full path to the saved file is returned.
|
|
func (s DownloaderTestSuite) saveSrcTestFile(f *TestFile) string {
|
|
path := filepath.Join(s.Home, "src")
|
|
fullName, err := f.SaveIn(path)
|
|
s.Require().NoError(err, "saving test file %s", f.Name)
|
|
return fullName
|
|
}
|
|
|
|
// requireFileExistsAndIsExecutable requires that the given file exists and is executable.
|
|
func requireFileExistsAndIsExecutable(t *testing.T, path string) {
|
|
info, err := os.Stat(path)
|
|
require.NoError(t, err, "stat error")
|
|
perm := info.Mode().Perm()
|
|
// Checks if at least one executable bit is set (user, group, or other)
|
|
isExe := perm&0111 != 0
|
|
require.True(t, isExe, "is executable: permissions = %s", perm)
|
|
}
|
|
|
|
// requireFileEquals requires that the contents of the file at the given path
|
|
// is equal to the contents of the given TestFile.
|
|
func requireFileEquals(t *testing.T, path string, tf *TestFile) {
|
|
file, err := os.ReadFile(path)
|
|
require.NoError(t, err, "reading file")
|
|
require.Equal(t, string(tf.Contents), string(file), "file contents")
|
|
}
|
|
|
|
// makeFileUrl converts the given path to a URL with the correct checksum query parameter.
|
|
func makeFileURL(t *testing.T, path string) string {
|
|
f, err := os.Open(path)
|
|
require.NoError(t, err, "opening file")
|
|
defer f.Close()
|
|
hasher := sha256.New()
|
|
_, err = io.Copy(hasher, f)
|
|
require.NoError(t, err, "copying file to hasher")
|
|
return fmt.Sprintf("file://%s?checksum=sha256:%x", path, hasher.Sum(nil))
|
|
}
|
|
|
|
func (s *DownloaderTestSuite) TestDownloadUpgrade() {
|
|
justAFile := NewTestFile("just-a-file", "#!/usr/bin\necho 'I am just a file'\n")
|
|
someFileName := "some-file"
|
|
someFileInBin := NewTestFile("bin"+someFileName, "#!/usr/bin\necho 'I am some file in bin'\n")
|
|
anotherFile := NewTestFile("another-file", "#!/usr/bin\necho 'I am just another file'\n")
|
|
justAFilePath := s.saveSrcTestFile(justAFile)
|
|
justAFileZip := s.saveSrcTestZip(justAFile.Name+".zip", NewTestZip(justAFile))
|
|
someFileInBinZip := s.saveSrcTestZip(someFileInBin.Name+".zip", NewTestZip(someFileInBin))
|
|
allFilesZip := s.saveSrcTestZip(anotherFile.Name+".zip", NewTestZip(justAFile, someFileInBin, anotherFile))
|
|
getDstDir := func(testName string) string {
|
|
_, tName := filepath.Split(testName)
|
|
return s.Home + "/dst/" + tName
|
|
}
|
|
|
|
s.T().Run("url does not exist", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
url := "file:///never/gonna/be/a/thing.zip?checksum=sha256:2c22e34510bd1d4ad2343cdc54f7165bccf30caef73f39af7dd1db2795a3da48"
|
|
err := DownloadUpgrade(dstRoot, url, "nothing")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no such file or directory")
|
|
})
|
|
|
|
s.T().Run("url does not have checksum", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
url := "file://" + justAFilePath
|
|
err := DownloadUpgrade(dstRoot, url, justAFile.Name)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "missing checksum query parameter")
|
|
})
|
|
|
|
s.T().Run("url has incorrect checksum", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
badChecksum := "2c22e34510bd1d4ad2343cdc54f7165bccf30caef73f39af7dd1db2795a3da48"
|
|
url := "file://" + justAFilePath + "?checksum=sha256:" + badChecksum
|
|
err := DownloadUpgrade(dstRoot, url, justAFile.Name)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Checksums did not match")
|
|
assert.Contains(t, err.Error(), "Expected: "+badChecksum)
|
|
})
|
|
|
|
s.T().Run("url returns single file", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
url := makeFileURL(t, justAFilePath)
|
|
err := DownloadUpgrade(dstRoot, url, justAFile.Name)
|
|
require.NoError(t, err)
|
|
expectedFile := filepath.Join(dstRoot, "bin", justAFile.Name)
|
|
requireFileExistsAndIsExecutable(t, expectedFile)
|
|
requireFileEquals(t, expectedFile, justAFile)
|
|
})
|
|
|
|
s.T().Run("url returns archive with file in bin", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
url := makeFileURL(t, someFileInBinZip)
|
|
err := DownloadUpgrade(dstRoot, url, someFileName)
|
|
require.NoError(t, err)
|
|
expectedFile := filepath.Join(dstRoot, "bin", someFileName)
|
|
requireFileExistsAndIsExecutable(t, expectedFile)
|
|
requireFileEquals(t, expectedFile, someFileInBin)
|
|
})
|
|
|
|
s.T().Run("url returns archive with just expected file", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
url := makeFileURL(t, justAFileZip)
|
|
err := DownloadUpgrade(dstRoot, url, justAFile.Name)
|
|
require.NoError(t, err)
|
|
expectedFile := filepath.Join(dstRoot, "bin", justAFile.Name)
|
|
requireFileExistsAndIsExecutable(t, expectedFile)
|
|
requireFileEquals(t, expectedFile, justAFile)
|
|
})
|
|
|
|
s.T().Run("url returns archive without expected file", func(t *testing.T) {
|
|
dstRoot := getDstDir(t.Name())
|
|
url := makeFileURL(t, allFilesZip)
|
|
err := DownloadUpgrade(dstRoot, url, "not-expected")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "result does not contain a bin/not-expected or not-expected file")
|
|
})
|
|
}
|
|
|
|
func (s *DownloaderTestSuite) TestEnsureBinary() {
|
|
nonExeName := s.saveSrcTestFile(NewTestFile("non-exe.txt", "Not executable"))
|
|
s.Require().NoError(os.Chmod(nonExeName, 0600), "chmod error nonExeName")
|
|
isExeName := s.saveSrcTestFile(NewTestFile("is-exe.sh", "#!/bin/bash\necho 'executing'\n"))
|
|
s.Require().NoError(os.Chmod(isExeName, 0777), "chmod error isExeName")
|
|
|
|
s.T().Run("file does not exist", func(t *testing.T) {
|
|
name := filepath.Join(s.Home, "does-not-exist.txt")
|
|
actual := EnsureBinary(name)
|
|
require.Error(t, actual)
|
|
})
|
|
|
|
s.T().Run("file is a directory", func(t *testing.T) {
|
|
name := filepath.Join(s.Home, "src")
|
|
actual := EnsureBinary(name)
|
|
require.EqualError(t, actual, fmt.Sprintf("%s is not a regular file", "src"))
|
|
})
|
|
|
|
s.T().Run("file exists and becomes executable", func(t *testing.T) {
|
|
name := nonExeName
|
|
actual := EnsureBinary(name)
|
|
require.NoError(t, actual, "EnsureBinary error")
|
|
requireFileExistsAndIsExecutable(t, name)
|
|
})
|
|
|
|
s.T().Run("file is already executable", func(t *testing.T) {
|
|
name := isExeName
|
|
actual := EnsureBinary(name)
|
|
require.NoError(t, actual, "EnsureBinary error")
|
|
requireFileExistsAndIsExecutable(t, name)
|
|
})
|
|
}
|
|
|
|
func (s *DownloaderTestSuite) TestDownloadURLWithChecksum() {
|
|
planContents := `{"binaries":{"xxx/yyy":"url"}}`
|
|
planFile := NewTestFile("plan-info.json", planContents)
|
|
planPath := s.saveSrcTestFile(planFile)
|
|
planChecksum := fmt.Sprintf("%x", sha256.Sum256(planFile.Contents))
|
|
emptyFile := NewTestFile("empty-plan-info.json", "")
|
|
emptyPlanPath := s.saveSrcTestFile(emptyFile)
|
|
emptyChecksum := fmt.Sprintf("%x", sha256.Sum256(emptyFile.Contents))
|
|
|
|
s.T().Run("url does not exist", func(t *testing.T) {
|
|
url := "file:///never-gonna-be-a-thing?checksum=sha256:2c22e34510bd1d4ad2343cdc54f7165bccf30caef73f39af7dd1db2795a3da48"
|
|
_, err := DownloadURLWithChecksum(url)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "could not download url")
|
|
})
|
|
|
|
s.T().Run("without checksum", func(t *testing.T) {
|
|
url := "file://" + planPath
|
|
_, err := DownloadURLWithChecksum(url)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "missing checksum query parameter")
|
|
})
|
|
|
|
s.T().Run("with correct checksum", func(t *testing.T) {
|
|
url := "file://" + planPath + "?checksum=sha256:" + planChecksum
|
|
actual, err := DownloadURLWithChecksum(url)
|
|
require.NoError(t, err)
|
|
require.Equal(t, planContents, actual)
|
|
})
|
|
|
|
s.T().Run("with incorrect checksum", func(t *testing.T) {
|
|
badChecksum := "2c22e34510bd1d4ad2343cdc54f7165bccf30caef73f39af7dd1db2795a3da48"
|
|
url := "file://" + planPath + "?checksum=sha256:" + badChecksum
|
|
_, err := DownloadURLWithChecksum(url)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Checksums did not match")
|
|
assert.Contains(t, err.Error(), "Expected: "+badChecksum)
|
|
assert.Contains(t, err.Error(), "Got: "+planChecksum)
|
|
})
|
|
|
|
s.T().Run("plan is empty", func(t *testing.T) {
|
|
url := "file://" + emptyPlanPath + "?checksum=sha256:" + emptyChecksum
|
|
_, err := DownloadURLWithChecksum(url)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no content returned")
|
|
})
|
|
}
|