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

Implement a POC folder downloader #2830

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/klauspost/cpuid v1.2.3
github.com/lucas-clemente/quic-go v0.15.2
github.com/manifoldco/promptui v0.7.0 // indirect
github.com/mholt/archiver/v3 v3.3.0
github.com/miekg/dns v1.1.29 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.1
Expand Down
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee h1:NYqDBPkhVYt68W3yoGoRRi32i3MLx2ey7SFkJ1v/UI0=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6 h1:bZ28Hqta7TFAK3Q08CMvv8y3/8ATaEqv2nGoc6yff6c=
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015 h1:StuiJFxQUsxSCzcby6NFZRdEhPkXD5vxN7TZ4MD6T84=
Expand Down Expand Up @@ -182,6 +184,8 @@ github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac h1:opbrjaN/L8gg6Xh5D04Tem+8xVcz6ajZlGCs49mQgyg=
Expand Down Expand Up @@ -247,6 +251,8 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -274,6 +280,7 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
Expand Down Expand Up @@ -398,12 +405,14 @@ github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7IL
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down Expand Up @@ -468,6 +477,7 @@ github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI=
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
Expand Down Expand Up @@ -516,6 +526,7 @@ github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIW
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
Expand Down Expand Up @@ -727,6 +738,7 @@ github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
Expand Down Expand Up @@ -755,6 +767,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
151 changes: 148 additions & 3 deletions modules/caddyhttp/fileserver/browse.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,42 @@ package fileserver
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io"
"net/http"
"os"
"path"
"path/filepath"
"strings"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/mholt/archiver/v3"
)

// Browse configures directory browsing.
type Browse struct {
// Use this template file instead of the default browse template.
TemplateFile string `json:"template_file,omitempty"`
TemplateFile string `json:"template_file,omitempty"`
DirArchives []string `json:"dir_archives,omitempty"`

template *template.Template
}

const (
zipContentType = "application/zip"
zipExtension = "zip"
tarContentType = "application/tar"
tarExtension = "tar"
tarGzipContentType = "application/tar+gzip"
tarGzipExtension = "tar.gz"
)

var (
extensionToContentType = map[string]string{zipExtension: zipContentType, tarExtension: tarContentType, tarGzipExtension: tarGzipContentType}
)

func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// navigation on the client-side gets messed up if the
// URL doesn't end in a trailing slash because hrefs like
Expand All @@ -46,14 +64,20 @@ func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *ht
return nil
}

repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
suffix := repl.ReplaceAll(r.URL.Path, "")
jaysonsantos marked this conversation as resolved.
Show resolved Hide resolved
contentType := r.URL.Query().Get("archive")

if contentType != "" {
return fsrv.streamFolderAsArchive(suffix, dirPath, contentType, w, r, next)
}
jaysonsantos marked this conversation as resolved.
Show resolved Hide resolved

dir, err := fsrv.openFile(dirPath, w)
if err != nil {
return err
}
defer dir.Close()

repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)

// calling path.Clean here prevents weird breadcrumbs when URL paths are sketchy like /%2e%2e%2f
listing, err := fsrv.loadDirectoryContents(dir, path.Clean(r.URL.Path), repl)
switch {
Expand Down Expand Up @@ -147,6 +171,98 @@ func (fsrv *FileServer) browseWriteHTML(listing browseListing) (*bytes.Buffer, e
return buf, err
}

func (fsrv *FileServer) streamFolderAsArchive(baseFolderName, downloadFolderName, extension string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
baseFolderName = baseFolderName[1:] // Remove initial slash
if err := validateExtension(extension); err != nil {
return writeUnsupportedMediaType(w, err)
}
contentType := extensionToContentType[extension]

writer, err := fsrv.getArchiveWriter(contentType)
if err != nil {
return writeUnsupportedMediaType(w, err)
}
defer writer.Close()

w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.%s\"", path.Base(baseFolderName), extension))
err = writer.Create(w)
if err != nil {
return err
}

downloadFolderInfo, err := os.Stat(downloadFolderName)
if err != nil {
if os.IsNotExist(err) {
return fsrv.notFound(w, r, next)
}
return err
}

err = filepath.Walk(downloadFolderName, func(fpath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info == nil {
return fmt.Errorf("nil file info")
}

// open the file, if it has any content
var file io.ReadCloser
if info.Mode().IsRegular() {
file, err = os.Open(fpath)
if err != nil {
return fmt.Errorf("%s: opening: %v", fpath, err)
}
defer file.Close()
}

// make its archive-internal name
internalName, err := archiver.NameInArchive(downloadFolderInfo, downloadFolderName, fpath)
if err != nil {
return fmt.Errorf("making internal archive name for %s: %v", fpath, err)
}

// write the file to the archive
err = writer.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: internalName,
},
ReadCloser: file,
})
if err != nil {
return fmt.Errorf("writing file to archive: %v", err)
}

return nil
})

if err != nil {
return fmt.Errorf("walking %s: %v", downloadFolderName, err)
}

return nil
}

func (fsrv *FileServer) getArchiveWriter(contentType string) (archiver.Writer, error) {
switch contentType {
default:
return nil, fmt.Errorf("A file format with content type %v is not supported", contentType)
case zipContentType:
return &archiver.Zip{
CompressionLevel: 0,
MkdirAll: true,
SelectiveCompression: true,
ImplicitTopLevelFolder: true,
}, nil
case tarContentType:
return &archiver.Tar{MkdirAll: true, ImplicitTopLevelFolder: true}, nil
case tarGzipContentType:
return &archiver.TarGz{Tar: &archiver.Tar{MkdirAll: true, ImplicitTopLevelFolder: true}}, nil
}
}

// isSymlink return true if f is a symbolic link
func isSymlink(f os.FileInfo) bool {
return f.Mode()&os.ModeSymlink != 0
Expand All @@ -165,3 +281,32 @@ func isSymlinkTargetDir(f os.FileInfo, root, urlPath string) bool {
}
return targetInfo.IsDir()
}

func validateArchiveSelection(extensions []string) error {
var invalidExtensions []string

for _, extension := range extensions {
if _, keyFound := extensionToContentType[extension]; !keyFound {
invalidExtensions = append(invalidExtensions, extension)
}
}

if len(invalidExtensions) == 0 {
return nil
}

return fmt.Errorf("these extensions are not valid choices %v", invalidExtensions)
}

func validateExtension(extension string) error {
if _, ok := extensionToContentType[extension]; !ok {
return fmt.Errorf("A file format with extension %v is not supported", extension)
}
return nil
}

func writeUnsupportedMediaType(w http.ResponseWriter, err error) error {
w.WriteHeader(415)
_, err = w.Write([]byte(fmt.Sprint(err)))
return err
}
80 changes: 80 additions & 0 deletions modules/caddyhttp/fileserver/browse_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package fileserver

import (
"github.com/mholt/archiver/v3"
"html/template"
"reflect"
"testing"

"github.com/caddyserver/caddy/v2"
Expand Down Expand Up @@ -52,3 +54,81 @@ func BenchmarkBrowseWriteHTML(b *testing.B) {
fsrv.browseWriteHTML(listing)
}
}

func TestFileServer_getArchiveWriter(t *testing.T) {
tests := []struct {
name string
contentType string
want interface{}
wantErr bool
}{
{name: "zip", contentType: "application/zip", want: new(archiver.Zip)},
{name: "tar", contentType: "application/tar", want: new(archiver.Tar)},
{name: "err", contentType: "application/wrong", wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fsrv := new(FileServer)
got, err := fsrv.getArchiveWriter(tt.contentType)
if (err != nil) != tt.wantErr {
t.Errorf("getArchiveWriter() error = %v, wantErr %v", err, tt.wantErr)
return
}
if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
t.Errorf("getArchiveWriter() got = %v, want %v", got, tt.want)
}
})
}

t.Run("make sure all archives are implemented", func(y *testing.T) {
fsrv := new(FileServer)
for _, mimeType := range extensionToContentType {
if _, err := fsrv.getArchiveWriter(mimeType); err != nil {
t.Errorf("archive writer is for content type %v is not implemented yet: %v", mimeType, err)
}
}
})
}

func Test_validateArchiveSelection(t *testing.T) {
type args struct {
extensions []string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"zip", args{[]string{"zip"}}, false},
{"xyz", args{[]string{"zip", "xyz"}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validateArchiveSelection(tt.args.extensions); (err != nil) != tt.wantErr {
t.Errorf("validateArchiveSelection() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func Test_validateExtension(t *testing.T) {
type args struct {
extension string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"zip", args{"zip"}, false},
{"tar", args{"tar"}, false},
{"error", args{"ar"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validateExtension(tt.args.extension); (err != nil) != tt.wantErr {
t.Errorf("validateExtension() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
Loading