diff --git a/packages/cdk/npm-shrinkwrap.json b/packages/cdk/npm-shrinkwrap.json index 7c12cd19..c9c4a4a0 100644 --- a/packages/cdk/npm-shrinkwrap.json +++ b/packages/cdk/npm-shrinkwrap.json @@ -1265,36 +1265,29 @@ }, "dependencies": { "@balena/dockerignore": { - "version": "1.0.2", - "bundled": true + "version": "1.0.2" }, "at-least-node": { - "version": "1.0.0", - "bundled": true + "version": "1.0.0" }, "balanced-match": { - "version": "1.0.2", - "bundled": true + "version": "1.0.2" }, "brace-expansion": { "version": "1.1.11", - "bundled": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "case": { - "version": "1.6.3", - "bundled": true + "version": "1.6.3" }, "concat-map": { - "version": "0.0.1", - "bundled": true + "version": "0.0.1" }, "fs-extra": { "version": "9.1.0", - "bundled": true, "requires": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -1303,61 +1296,50 @@ } }, "graceful-fs": { - "version": "4.2.8", - "bundled": true + "version": "4.2.8" }, "ignore": { - "version": "5.1.9", - "bundled": true + "version": "5.1.9" }, "jsonfile": { "version": "6.1.0", - "bundled": true, "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" } }, "jsonschema": { - "version": "1.4.0", - "bundled": true + "version": "1.4.0" }, "lru-cache": { "version": "6.0.0", - "bundled": true, "requires": { "yallist": "^4.0.0" } }, "minimatch": { "version": "3.0.4", - "bundled": true, "requires": { "brace-expansion": "^1.1.7" } }, "punycode": { - "version": "2.1.1", - "bundled": true + "version": "2.1.1" }, "semver": { "version": "7.3.5", - "bundled": true, "requires": { "lru-cache": "^6.0.0" } }, "universalify": { - "version": "2.0.0", - "bundled": true + "version": "2.0.0" }, "yallist": { - "version": "4.0.0", - "bundled": true + "version": "4.0.0" }, "yaml": { - "version": "1.10.2", - "bundled": true + "version": "1.10.2" } } }, diff --git a/packages/cli/.agcignore b/packages/cli/.agcignore new file mode 100644 index 00000000..b5131741 --- /dev/null +++ b/packages/cli/.agcignore @@ -0,0 +1,3 @@ +.nextflow +.snakemake +work \ No newline at end of file diff --git a/packages/cli/go.sum b/packages/cli/go.sum index 1ff1cfa4..04af368c 100644 --- a/packages/cli/go.sum +++ b/packages/cli/go.sum @@ -11,6 +11,7 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.61.0/go.mod h1:XukKJg4Y7QsUu0Hxg3qQKUWR4VuWivmyMK2+rUyxAqw= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= @@ -27,6 +28,7 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.3.0/go.mod h1:Qt0gS9Qz9tROrmgFavo36+hdST1FXvmtnGnO0Dr03pU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -341,6 +343,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/weathersource/go-errors v1.0.1/go.mod h1:6AhTSfdYXpt+kdI9e3N9Beak4vH8Ye/e5+sTazseyPo= +github.com/weathersource/go-gsrv v1.0.1/go.mod h1:yOOB7n+UMhC43FApo/MYhspWrm325uR5aWhTL91tOag= +github.com/weathersource/go-mockfs v1.0.1/go.mod h1:e2mvTTiHoiD/Xj3nPDY5gVqvdW/2uY1vnZWlEflCsNk= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -441,6 +446,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200923182212-328152dc79b1/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -577,6 +583,8 @@ golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200727233628-55644ead90ce/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -613,6 +621,7 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= @@ -654,10 +663,13 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200728010541-3dc8dca74b7b/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200924141100-a14c0a98937d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -681,6 +693,7 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= diff --git a/packages/cli/internal/pkg/mocks/io/interfaces.go b/packages/cli/internal/pkg/mocks/io/interfaces.go index be2541bd..04bd2529 100644 --- a/packages/cli/internal/pkg/mocks/io/interfaces.go +++ b/packages/cli/internal/pkg/mocks/io/interfaces.go @@ -1,7 +1,9 @@ package iomocks import ( + "io" "io/fs" + "os" "time" "github.com/aws/amazon-genomics-cli/internal/pkg/cli/spec" @@ -17,6 +19,12 @@ type OS interface { Stat(name string) (fs.FileInfo, error) MkdirAll(path string, perm fs.FileMode) error IsNotExist(err error) bool + Create(name string) (*os.File, error) + Open(name string) (*os.File, error) +} + +type IO interface { + Copy(dst io.Writer, src io.Reader) (written int64, err error) } type FileInfo interface { diff --git a/packages/cli/internal/pkg/mocks/io/mock_interfaces.go b/packages/cli/internal/pkg/mocks/io/mock_interfaces.go index 916431b0..9a3769d8 100644 --- a/packages/cli/internal/pkg/mocks/io/mock_interfaces.go +++ b/packages/cli/internal/pkg/mocks/io/mock_interfaces.go @@ -5,7 +5,9 @@ package iomocks import ( + io "io" fs "io/fs" + os "os" reflect "reflect" time "time" @@ -51,6 +53,21 @@ func (mr *MockOSMockRecorder) Chdir(dir interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Chdir", reflect.TypeOf((*MockOS)(nil).Chdir), dir) } +// Create mocks base method. +func (m *MockOS) Create(name string) (*os.File, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", name) + ret0, _ := ret[0].(*os.File) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockOSMockRecorder) Create(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockOS)(nil).Create), name) +} + // IsNotExist mocks base method. func (m *MockOS) IsNotExist(err error) bool { m.ctrl.T.Helper() @@ -94,6 +111,21 @@ func (mr *MockOSMockRecorder) MkdirTemp(dir, pattern interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MkdirTemp", reflect.TypeOf((*MockOS)(nil).MkdirTemp), dir, pattern) } +// Open mocks base method. +func (m *MockOS) Open(name string) (*os.File, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Open", name) + ret0, _ := ret[0].(*os.File) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Open indicates an expected call of Open. +func (mr *MockOSMockRecorder) Open(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockOS)(nil).Open), name) +} + // Remove mocks base method. func (m *MockOS) Remove(name string) error { m.ctrl.T.Helper() @@ -152,6 +184,44 @@ func (mr *MockOSMockRecorder) UserHomeDir() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserHomeDir", reflect.TypeOf((*MockOS)(nil).UserHomeDir)) } +// MockIO is a mock of IO interface. +type MockIO struct { + ctrl *gomock.Controller + recorder *MockIOMockRecorder +} + +// MockIOMockRecorder is the mock recorder for MockIO. +type MockIOMockRecorder struct { + mock *MockIO +} + +// NewMockIO creates a new mock instance. +func NewMockIO(ctrl *gomock.Controller) *MockIO { + mock := &MockIO{ctrl: ctrl} + mock.recorder = &MockIOMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIO) EXPECT() *MockIOMockRecorder { + return m.recorder +} + +// Copy mocks base method. +func (m *MockIO) Copy(dst io.Writer, src io.Reader) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Copy", dst, src) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Copy indicates an expected call of Copy. +func (mr *MockIOMockRecorder) Copy(dst, src interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockIO)(nil).Copy), dst, src) +} + // MockFileInfo is a mock of FileInfo interface. type MockFileInfo struct { ctrl *gomock.Controller diff --git a/packages/cli/internal/pkg/osutils/utils.go b/packages/cli/internal/pkg/osutils/utils.go index 833b1779..8753ba4a 100644 --- a/packages/cli/internal/pkg/osutils/utils.go +++ b/packages/cli/internal/pkg/osutils/utils.go @@ -1,6 +1,7 @@ package osutils import ( + "bufio" "fmt" "io" "io/fs" @@ -20,6 +21,8 @@ var osCreate = os.Create var ioCopy = io.Copy var filepathWalkDir = filepath.WalkDir +const agcignore = "./.agcignore" + // DetermineHomeDir returns the file system directory where the AGC files live. func DetermineHomeDir() (string, error) { dir, err := osUserHomeDir() @@ -63,13 +66,27 @@ func EnsureDirExistence(dirPath string) error { } func CopyFileRecursivelyToLocation(absoluteDestinationDir string, absoluteSourceDir string) error { - err := filepathWalkDir(absoluteSourceDir, func(currentPath string, dirEntry fs.DirEntry, err error) error { + err := filepathWalkDir(absoluteSourceDir, getWalkDirFn(absoluteDestinationDir, absoluteSourceDir)) + return err +} + +func getWalkDirFn(absoluteDestinationDir string, absoluteSourceDir string) fs.WalkDirFunc { + return func(currentPath string, dirEntry fs.DirEntry, err error) error { if dirEntry == nil { // There are several use cases when it can happen: // 1. provided path doesn't exist // 2. file or sub-directory got deleted after being listed by WalkDir return fmt.Errorf("file '%s' doesn't exist", currentPath) } + ignoreDir, err := isDirIgnored(dirEntry.Name()) + if err != nil { + return err + } + if ignoreDir || + strings.HasSuffix(currentPath, ".nextflow.log") { + return filepath.SkipDir + } + if !dirEntry.IsDir() { srcFile, err := osOpen(currentPath) if err != nil { @@ -91,9 +108,30 @@ func CopyFileRecursivelyToLocation(absoluteDestinationDir string, absoluteSource } } return nil - }) + } +} - return err +func isDirIgnored(dirName string) (bool, error) { + if _, err := osStat(agcignore); err != nil { + return false, nil + } + file, err := os.Open(agcignore) + if err != nil { + return false, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if dirName == scanner.Text() { + return true, nil + } + } + + if err := scanner.Err(); err != nil { + return false, err + } + return false, nil } func getAndCreateRelativePath(currentPath string, sourcePath string, destinationDir string) (string, error) { diff --git a/packages/cli/internal/pkg/osutils/utils_test.go b/packages/cli/internal/pkg/osutils/utils_test.go index b0945a97..4eedf5af 100644 --- a/packages/cli/internal/pkg/osutils/utils_test.go +++ b/packages/cli/internal/pkg/osutils/utils_test.go @@ -2,6 +2,9 @@ package osutils import ( "errors" + "io/fs" + "os" + "path/filepath" "testing" "github.com/aws/amazon-genomics-cli/internal/pkg/cli/clierror/actionableerror" @@ -169,10 +172,10 @@ func TestGetAndCreateRelativePath(t *testing.T) { osMkdirAll = mocksUtils.mockOs.MkdirAll osStat = mocksUtils.mockOs.Stat osIsNotExist = mocksUtils.mockOs.IsNotExist - mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path", gomock.Any()).Return(nil).Times(1) + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path", gomock.Any()).Return(nil) doesNotExist := errors.New("Some error") - mocksUtils.mockOs.EXPECT().Stat("/some/other/path").Return(nil, doesNotExist).Times(1) - mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true).Times(1) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) }, currentPath: "/some/path/to/folder/file.ext", sourcePath: "/some/path/to/folder", @@ -186,9 +189,9 @@ func TestGetAndCreateRelativePath(t *testing.T) { osStat = mocksUtils.mockOs.Stat osIsNotExist = mocksUtils.mockOs.IsNotExist doesNotExist := errors.New("Some error") - mocksUtils.mockOs.EXPECT().Stat("/some/other/path/subfolder").Return(nil, doesNotExist).Times(1) - mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true).Times(1) - mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path/subfolder", gomock.Any()).Return(nil).Times(1) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path/subfolder").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path/subfolder", gomock.Any()).Return(nil) }, currentPath: "/some/path/to/folder/subfolder/file.ext", sourcePath: "/some/path/to/folder", @@ -202,9 +205,9 @@ func TestGetAndCreateRelativePath(t *testing.T) { osStat = mocksUtils.mockOs.Stat osIsNotExist = mocksUtils.mockOs.IsNotExist doesNotExist := errors.New("Some error") - mocksUtils.mockOs.EXPECT().Stat("/some/other/path/some/path/to/folder/subfolder").Return(nil, doesNotExist).Times(1) - mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true).Times(1) - mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path/some/path/to/folder/subfolder", gomock.Any()).Return(nil).Times(1) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path/some/path/to/folder/subfolder").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path/some/path/to/folder/subfolder", gomock.Any()).Return(nil) }, currentPath: "/some/path/to/folder/some/path/to/folder/subfolder/file.ext", sourcePath: "/some/path/to/folder", @@ -218,10 +221,10 @@ func TestGetAndCreateRelativePath(t *testing.T) { osStat = mocksUtils.mockOs.Stat osIsNotExist = mocksUtils.mockOs.IsNotExist doesNotExist := errors.New("Some error") - mocksUtils.mockOs.EXPECT().Stat("/some/other/path/subfolder").Return(nil, doesNotExist).Times(1) - mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true).Times(1) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path/subfolder").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) returnedError := errors.New("failed to create") - mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path/subfolder", gomock.Any()).Return(returnedError).Times(1) + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path/subfolder", gomock.Any()).Return(returnedError) }, currentPath: "/some/path/to/folder/subfolder/file.ext", sourcePath: "/some/path/to/folder", @@ -252,7 +255,284 @@ func TestGetAndCreateRelativePath(t *testing.T) { } } +func TestGetWalkDirFn(t *testing.T) { + backupOsStat, backupOsIsNotExist, backupOsMkdirAll, backupOsCreate, backupIoCopy, backupOsOpen := osStat, osIsNotExist, osMkdirAll, osCreate, ioCopy, osOpen + + defer func() { + osStat, osIsNotExist, osMkdirAll, osCreate, ioCopy, osOpen = backupOsStat, backupOsIsNotExist, backupOsMkdirAll, backupOsCreate, backupIoCopy, backupOsOpen + }() + + var tests = map[string]struct { + setupMocks func(mockUtils MockUtils) + currentPath string + absSourceDir string + absDestinationDir string + expectedErr error + dirEntry fs.DirEntry + }{ + "empty directory returns nil": { + setupMocks: func(mocksUtils MockUtils) {}, + currentPath: "/some/path/to/folder/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/other/path", + expectedErr: errors.New("file doesn't exist"), + dirEntry: nil, + }, + "it skips file: '.nextflow'": { + setupMocks: func(mocksUtils MockUtils) {}, + currentPath: "/some/path/to/folder/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/path/to", + expectedErr: filepath.SkipDir, + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: ".nextflow", + }, + }, + "it skips file: '.snakemake'": { + setupMocks: func(mocksUtils MockUtils) {}, + currentPath: "/some/path/to/folder/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/path/to", + expectedErr: filepath.SkipDir, + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: ".snakemake", + }, + }, + "it skips file: 'work'": { + setupMocks: func(mocksUtils MockUtils) {}, + currentPath: "/some/path/to/folder/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/path/to", + expectedErr: filepath.SkipDir, + dirEntry: TestDirEntry{ + isDir: true, + fileMode: 32, + info: nil, + infoError: nil, + name: "work", + }, + }, + "it skips suffix file: '.nextflow.log'": { + setupMocks: func(mocksUtils MockUtils) {}, + currentPath: "/some/path/to/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/path/to", + expectedErr: filepath.SkipDir, + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: "some/path/file.nextflow.log", + }, + }, + "if it tries to open an invalid file, it fails - OsOpen": { + setupMocks: func(mocksUtils MockUtils) { + osOpen = mocksUtils.mockOs.Open + var srcFile = &os.File{} + mocksUtils.mockOs.EXPECT().Open("/some/path/to/file.ext").Return(srcFile, errors.New("error here")) + }, + currentPath: "/some/path/to/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/other/path", + expectedErr: errors.New("error here"), + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: "some dir here", + }, + }, + "if its a real directory - returns nil": { + setupMocks: func(mocksUtils MockUtils) {}, + currentPath: "/some/path/to/folder", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/other/path", + expectedErr: nil, + dirEntry: TestDirEntry{ + isDir: true, + fileMode: 32, + info: nil, + infoError: nil, + name: "some dir here", + }, + }, + "get relative path fails": { + setupMocks: func(mocksUtils MockUtils) { + osStat = mocksUtils.mockOs.Stat + osIsNotExist = mocksUtils.mockOs.IsNotExist + osMkdirAll = mocksUtils.mockOs.MkdirAll + osOpen = mocksUtils.mockOs.Open + var srcFile = &os.File{} + doesNotExist := errors.New("Some error") + mocksUtils.mockOs.EXPECT().Stat("/some/other/path").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().Open("/some/path/to/folder/file.ext").Return(srcFile, nil) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) + returnedError := errors.New("some error") + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path", gomock.Any()).Return(returnedError) + }, + currentPath: "/some/path/to/folder/file.ext", + absSourceDir: "/some/path/to/folder", + absDestinationDir: "/some/other/path", + expectedErr: errors.New("some other error"), + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: "", + }, + }, + "if it tries to create a file, it fails - OsCreate": { + setupMocks: func(mocksUtils MockUtils) { + osMkdirAll = mocksUtils.mockOs.MkdirAll + osCreate = mocksUtils.mockOs.Create + osStat = mocksUtils.mockOs.Stat + osIsNotExist = mocksUtils.mockOs.IsNotExist + osOpen = mocksUtils.mockOs.Open + var srcFile = &os.File{} + var dstFile = &os.File{} + doesNotExist := errors.New("Some error") + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path", gomock.Any()).Return(nil) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) + mocksUtils.mockOs.EXPECT().Open("/some/path/to/file.ext").Return(srcFile, nil) + mocksUtils.mockOs.EXPECT().Create("/some/other/path/file.ext").Return(dstFile, errors.New("new error")) + }, + currentPath: "/some/path/to/file.ext", + absSourceDir: "/some/path/to", + absDestinationDir: "/some/other/path", + expectedErr: errors.New("new error"), + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: "", + }, + }, + "tries to create a file, it fails - IoCopy": { + setupMocks: func(mocksUtils MockUtils) { + osMkdirAll = mocksUtils.mockOs.MkdirAll + osCreate = mocksUtils.mockOs.Create + osStat = mocksUtils.mockOs.Stat + osIsNotExist = mocksUtils.mockOs.IsNotExist + osOpen = mocksUtils.mockOs.Open + ioCopy = mocksUtils.mockIo.Copy + var srcFile = &os.File{} + var dstFile = &os.File{} + doesNotExist := errors.New("Some error") + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path", gomock.Any()).Return(nil) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) + mocksUtils.mockOs.EXPECT().Open("/some/path/to/file.ext").Return(srcFile, nil) + mocksUtils.mockOs.EXPECT().Create("/some/other/path/file.ext").Return(dstFile, nil) + mocksUtils.mockIo.EXPECT().Copy(gomock.Any(), gomock.Any()).Return(int64(1), errors.New("new err")) + }, + currentPath: "/some/path/to/file.ext", + absSourceDir: "/some/path/to", + absDestinationDir: "/some/other/path", + expectedErr: errors.New("new err"), + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: "", + }, + }, + "GetWalkDirFn - happy path": { + setupMocks: func(mocksUtils MockUtils) { + osMkdirAll = mocksUtils.mockOs.MkdirAll + osCreate = mocksUtils.mockOs.Create + osStat = mocksUtils.mockOs.Stat + osIsNotExist = mocksUtils.mockOs.IsNotExist + osOpen = mocksUtils.mockOs.Open + ioCopy = mocksUtils.mockIo.Copy + var srcFile = &os.File{} + var dstFile = &os.File{} + doesNotExist := errors.New("Some error") + mocksUtils.mockOs.EXPECT().MkdirAll("/some/other/path", gomock.Any()).Return(nil) + mocksUtils.mockOs.EXPECT().Stat("/some/other/path").Return(nil, doesNotExist) + mocksUtils.mockOs.EXPECT().IsNotExist(doesNotExist).Return(true) + mocksUtils.mockOs.EXPECT().Open("/some/path/to/file.ext").Return(srcFile, nil) + mocksUtils.mockOs.EXPECT().Create("/some/other/path/file.ext").Return(dstFile, nil) + mocksUtils.mockIo.EXPECT().Copy(gomock.Any(), gomock.Any()).Return(int64(1), nil) + }, + currentPath: "/some/path/to/file.ext", + absSourceDir: "/some/path/to", + absDestinationDir: "/some/other/path", + expectedErr: nil, + dirEntry: TestDirEntry{ + isDir: false, + fileMode: 32, + info: nil, + infoError: nil, + name: "", + }, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockOs := iomocks.NewMockOS(ctrl) + mockIo := iomocks.NewMockIO(ctrl) + mockUtils := &MockUtils{ + ctrl: ctrl, + mockOs: mockOs, + mockIo: mockIo, + } + tt.setupMocks(*mockUtils) + + walkDirFunc := getWalkDirFn(tt.absDestinationDir, tt.absSourceDir) + err := walkDirFunc(tt.currentPath, tt.dirEntry, nil) + + if tt.expectedErr != nil { + assert.Error(t, err, tt.expectedErr.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + type MockUtils struct { ctrl *gomock.Controller mockOs *iomocks.MockOS + mockIo *iomocks.MockIO +} + +type TestDirEntry struct { + isDir bool + fileMode fs.FileMode + info fs.FileInfo + infoError error + name string +} + +func (tde TestDirEntry) IsDir() bool { + return tde.isDir +} + +func (tde TestDirEntry) Type() fs.FileMode { + return tde.fileMode +} + +func (tde TestDirEntry) Info() (fs.FileInfo, error) { + return tde.info, tde.infoError +} + +func (tde TestDirEntry) Name() string { + return tde.name }