* Use mholt/archives instead of mholt/archiver * Fix validations * Fix iszip function * Fix directory * Add better path sanitization * ensure safe dir is passed * Fix file permissions * Fix config path * Sanitize builder source path --------- Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
355 lines
7.7 KiB
Go
355 lines
7.7 KiB
Go
package utils
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestIsZip(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupFn func() string
|
|
want bool
|
|
wantErr bool
|
|
cleanup bool
|
|
}{
|
|
{
|
|
name: "valid zip file",
|
|
setupFn: func() string {
|
|
return "testdata/test.zip"
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
cleanup: false,
|
|
},
|
|
{
|
|
name: "non-existent file",
|
|
setupFn: func() string {
|
|
return "testdata/non-existent.zip"
|
|
},
|
|
want: false,
|
|
wantErr: false,
|
|
cleanup: true,
|
|
},
|
|
{
|
|
name: "text file",
|
|
setupFn: func() string {
|
|
f, err := os.CreateTemp("", "test-*.txt")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
if _, err := f.WriteString("hello world"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return f.Name()
|
|
},
|
|
want: false,
|
|
wantErr: false,
|
|
cleanup: true,
|
|
},
|
|
{
|
|
name: "corrupt zip file",
|
|
setupFn: func() string {
|
|
f, err := os.CreateTemp("", "corrupt-*.zip")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
if _, err := f.WriteString("corrupted content"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return f.Name()
|
|
},
|
|
want: true,
|
|
wantErr: false,
|
|
cleanup: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
filename := tt.setupFn()
|
|
if tt.cleanup && !filepath.IsAbs(filename) {
|
|
// Cleanup only temp files
|
|
t.Cleanup(func() {
|
|
os.Remove(filename)
|
|
})
|
|
}
|
|
|
|
got, err := IsZip(context.Background(), filename)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("IsZip() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("IsZip() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestArchiveUnarchive(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create temp test directories
|
|
sourceDir, err := os.MkdirTemp("", "zip-test-source-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(sourceDir)
|
|
|
|
// Create test files and directories
|
|
files := map[string][]byte{
|
|
"file1.txt": []byte("hello world"),
|
|
"file2.txt": []byte("test content"),
|
|
"dir1/file3.txt": []byte("nested file"),
|
|
"dir1/dir2/file4.txt": []byte("deeply nested"),
|
|
}
|
|
|
|
for path, content := range files {
|
|
fullPath := filepath.Join(sourceDir, path)
|
|
err := os.MkdirAll(filepath.Dir(fullPath), 0755)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = os.WriteFile(fullPath, content, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Create empty directory
|
|
emptyDir := filepath.Join(sourceDir, "empty-dir")
|
|
if err := os.Mkdir(emptyDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
srcPath string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "archive and unarchive directory",
|
|
srcPath: sourceDir,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "archive and unarchive single file",
|
|
srcPath: filepath.Join(sourceDir, "file1.txt"),
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Create temp zip file
|
|
zipFile, err := os.CreateTemp("", "test-*.zip")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
zipFile.Close()
|
|
defer os.Remove(zipFile.Name())
|
|
|
|
// Create temp extract directory
|
|
extractDir, err := os.MkdirTemp("", "zip-test-extract-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(extractDir)
|
|
|
|
// Test Archive
|
|
err = Archive(ctx, tt.srcPath, zipFile.Name())
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Archive() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
// Test is valid zip file
|
|
isZip, err := IsZip(ctx, zipFile.Name())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !isZip {
|
|
t.Errorf("Archive() did not create a valid zip file")
|
|
return
|
|
}
|
|
|
|
// Test Unarchive
|
|
err = Unarchive(ctx, zipFile.Name(), extractDir)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Unarchive() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
// Validate extracted content
|
|
err = filepath.Walk(tt.srcPath, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
relPath, err := filepath.Rel(tt.srcPath, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if relPath == "." {
|
|
return nil
|
|
}
|
|
|
|
extractedPath := filepath.Join(extractDir, relPath)
|
|
|
|
extractedInfo, err := os.Stat(extractedPath)
|
|
if err != nil {
|
|
t.Errorf("Expected file %s not found in extracted directory", relPath)
|
|
return nil
|
|
}
|
|
|
|
if info.Mode().Perm() != extractedInfo.Mode().Perm() {
|
|
t.Errorf("File %s permissions mismatch: got %v, want %v",
|
|
relPath, extractedInfo.Mode().Perm(), info.Mode().Perm())
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
originalContent, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
extractedContent, err := os.ReadFile(extractedPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(originalContent) != string(extractedContent) {
|
|
t.Errorf("File %s content mismatch", relPath)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestArchiveOverwrite(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create initial source directory
|
|
sourceDir, err := os.MkdirTemp("", "zip-test-source-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(sourceDir)
|
|
|
|
// Create initial files
|
|
initialFiles := map[string][]byte{
|
|
"old1.txt": []byte("old content 1"),
|
|
"old2.txt": []byte("old content 2"),
|
|
}
|
|
for path, content := range initialFiles {
|
|
fullPath := filepath.Join(sourceDir, path)
|
|
if err := os.WriteFile(fullPath, content, 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Create zip file
|
|
zipFile, err := os.CreateTemp("", "test-*.zip")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
zipFile.Close()
|
|
defer os.Remove(zipFile.Name())
|
|
|
|
// Create initial zip
|
|
if err := Archive(ctx, sourceDir, zipFile.Name()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create new source directory with different content
|
|
newSourceDir, err := os.MkdirTemp("", "zip-test-new-source-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(newSourceDir)
|
|
|
|
// Create new files
|
|
newFiles := map[string][]byte{
|
|
"new1.txt": []byte("new content 1"),
|
|
"new2.txt": []byte("new content 2"),
|
|
}
|
|
for path, content := range newFiles {
|
|
fullPath := filepath.Join(newSourceDir, path)
|
|
if err := os.WriteFile(fullPath, content, 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Overwrite existing zip
|
|
if err := Archive(ctx, newSourceDir, zipFile.Name()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create extraction directory
|
|
extractDir, err := os.MkdirTemp("", "zip-test-extract-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(extractDir)
|
|
|
|
// Extract overwritten zip
|
|
if err := Unarchive(ctx, zipFile.Name(), extractDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Validate extracted content
|
|
files, err := os.ReadDir(extractDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Verify only new files exist
|
|
expectedFiles := map[string]bool{
|
|
"new1.txt": false,
|
|
"new2.txt": false,
|
|
}
|
|
|
|
for _, f := range files {
|
|
if _, ok := expectedFiles[f.Name()]; !ok {
|
|
t.Errorf("Unexpected file found: %s", f.Name())
|
|
continue
|
|
}
|
|
expectedFiles[f.Name()] = true
|
|
|
|
// Verify content
|
|
content, err := os.ReadFile(filepath.Join(extractDir, f.Name()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := newFiles[f.Name()]
|
|
if string(content) != string(expected) {
|
|
t.Errorf("File %s content mismatch: got %s, want %s",
|
|
f.Name(), string(content), string(expected))
|
|
}
|
|
}
|
|
|
|
// Verify old files do not exist
|
|
oldFiles := []string{"old1.txt", "old2.txt"}
|
|
for _, oldFile := range oldFiles {
|
|
_, err := os.Stat(filepath.Join(extractDir, oldFile))
|
|
if !os.IsNotExist(err) {
|
|
t.Errorf("Old file %s should not exist in zip", oldFile)
|
|
}
|
|
}
|
|
|
|
// Verify all expected files were found
|
|
for name, found := range expectedFiles {
|
|
if !found {
|
|
t.Errorf("Expected file not found: %s", name)
|
|
}
|
|
}
|
|
}
|