From 91da820007112fbc6928376bd43ac3228d776b11 Mon Sep 17 00:00:00 2001 From: Chris Berthiaume Date: Fri, 6 Dec 2019 18:36:08 -0800 Subject: [PATCH] Add flag for earliest file to transfer --- build.sh | 9 ++ cmd/seaflow-transfer/main.go | 25 +++- go.sum | 2 + internal/fs/fs.go | 151 ++++++++++++++++-------- internal/fs/fs_test.go | 214 ++++++++++++++++++++++++++++++++--- 5 files changed, 334 insertions(+), 67 deletions(-) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..52ae6ed --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Build seaflow-transfer command-line tool for 64-bit MacOS and Linux + +[[ -d seaflow-transfer.darwin-amd64 ]] && rm -rf seaflow-transfer.darwin-amd64 +[[ -d seaflow-transfer.linux-amd64 ]] && rm -rf seaflow-transfer.linux-amd64 +GOOS=darwin GOARCH=amd64 go build -o seaflow-transfer.darwin-amd64/seaflow-transfer cmd/seaflow-transfer/main.go || exit 1 +GOOS=linux GOARCH=amd64 go build -o seaflow-transfer.linux-amd64/seaflow-transfer cmd/seaflow-transfer/main.go || exit 1 +zip -q -r seaflow-transfer.darwin-amd64.zip seaflow-transfer.darwin-amd64 || exit 1 +zip -q -r seaflow-transfer.linux-amd64.zip seaflow-transfer.linux-amd64 || exit 1 diff --git a/cmd/seaflow-transfer/main.go b/cmd/seaflow-transfer/main.go index f16a42a..3f9a62c 100644 --- a/cmd/seaflow-transfer/main.go +++ b/cmd/seaflow-transfer/main.go @@ -7,12 +7,13 @@ import ( "log" "os" "syscall" + "time" "github.com/armbrustlab/seaflow-transfer/internal/fs" "golang.org/x/crypto/ssh/terminal" ) -const versionStr string = "v0.1.0" +const versionStr string = "v0.2.0" var ( srcRoot string // SRCROOT @@ -24,9 +25,10 @@ var ( sshPassword string // SSHPASSWORD sshPublicKey string // SSHPUBLICKEY quiet bool // QUIET + start string // START version bool // VERSION - ) +var t0 time.Time var cmdname string = "seaflow-transfer" func init() { @@ -44,6 +46,13 @@ func init() { } sshPassword = string(b) } + if start != "" { + var err error + t0, err = time.Parse(time.RFC3339, start) + if err != nil { + log.Fatalf("could not parse -start RFC3339 timestamp: %v", err) + } + } } func initFlags() { @@ -56,6 +65,7 @@ func initFlags() { flagset.StringVar(&sshUser, "sshUser", "", "SSH user name") flagset.StringVar(&sshPublicKey, "sshPublicKey", "", "SSH public key file, overrides SSHPASSWORD") flagset.BoolVar(&quiet, "quiet", false, "Suppress informational logging") + flagset.StringVar(&start, "start", "", "Earliest file timestamp to transfer as an RFC3339 string") flagset.BoolVar(&version, "version", false, "Display version and exit") flagset.Usage = func() { @@ -113,6 +123,10 @@ func initEnvVars() { if ok && val == "1" { quiet = true } + val, ok = os.LookupEnv("START") + if ok { + start = val + } val, ok = os.LookupEnv("VERSION") if ok && val == "1" { version = true @@ -126,9 +140,10 @@ func main() { } t := &fs.Transfer{ - Srcroot: srcRoot, - Dstroot: dstRoot, - Info: logger, + Srcroot: srcRoot, + Dstroot: dstRoot, + Info: logger, + Earliest: t0, } var err error if srcAddress != "" { diff --git a/go.sum b/go.sum index 263ff95..c0d264e 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -20,6 +21,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 66ab641..74a32eb 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -10,6 +10,7 @@ import ( "math/rand" "os" "path/filepath" + "regexp" "sort" "time" @@ -116,12 +117,14 @@ func (l Localfs) rename(oldname, newname string) error { // Transfer provides methods to copy SeaFlow data from a source to a destination // location type Transfer struct { - Srcfs Fs - Srcroot string - Dstfs Fs - Dstroot string - Info *log.Logger - rand *rand.Rand // for temp file names + Srcfs Fs + Srcroot string + Dstfs Fs + Dstroot string + Info *log.Logger + rand *rand.Rand // for temp file names + Earliest time.Time // earliest file time to transfer + //Latest time.Time // latest file time to transfer } // CopySFLFiles copies SFL files from source to destination. Files are @@ -135,7 +138,16 @@ func (t *Transfer) CopySFLFiles() error { } t.Info.Printf("found %v source SFL files\n", len(srcFiles)) for _, path := range srcFiles { - err := t.CopyFile(path, false) + if !t.Earliest.IsZero() { + filetime, err := timeFromFilename(path) + if err == nil && filetime.Before(t.Earliest) { + t.Info.Printf("skipping %v: %v < %v\n", path, filetime, t.Earliest) + continue + } + // otherwise default to transferring files that don't have parseable + // timestamps or are not before t.Earliest + } + err = t.CopyFile(path, false) if err != nil { return fmt.Errorf("error while copying %v: %v", path, err) } @@ -158,55 +170,83 @@ func (t *Transfer) CopyEVTFiles() error { panic(err) } t.Info.Printf("found %v source EVT files\n", len(srcFiles)) - if len(srcFiles) > 1 { - // Copy all but the latest EVT file since it's most likely currently - // being appended to. It's possible to identify the latest file here as - // the last in the array after a lexicographical sort, which sorts - // timestamped SeaFlow EVT files chronologically. - sort.Strings(srcFiles) - srcFiles = srcFiles[:len(srcFiles)-1] - dstPattern := filepath.Join(t.Dstroot, "????_???", "????-??-??T??-??-??[\\-\\+]??-??") - dstFiles, err := t.Dstfs.glob(dstPattern) - if err != nil { - panic(err) - } - dstFilesgz, err := t.Dstfs.glob(dstPattern + ".gz") - if err != nil { - panic(err) + + if len(srcFiles) <= 1 { + return nil + } + + // Copy all but the latest EVT file since it's most likely currently + // being appended to. It's possible to identify the latest file here as + // the last in the array after a lexicographical sort, which sorts + // timestamped SeaFlow EVT files chronologically. + sort.Strings(srcFiles) + srcFiles = srcFiles[:len(srcFiles)-1] + dstPattern := filepath.Join(t.Dstroot, "????_???", "????-??-??T??-??-??[\\-\\+]??-??") + dstFiles, err := t.Dstfs.glob(dstPattern) + if err != nil { + panic(err) + } + dstFilesgz, err := t.Dstfs.glob(dstPattern + ".gz") + if err != nil { + panic(err) + } + dstFiles = append(dstFiles, dstFilesgz...) + // Skip EVT files already present in destination + present := make(map[string]bool) + for _, path := range dstFiles { + pathgz := path + if filepath.Ext(path) == ".gz" { + path = path[:len(path)-len(".gz")] + } else { + pathgz = pathgz + ".gz" } - dstFiles = append(dstFiles, dstFilesgz...) - // Skip EVT files already present in destination - present := make(map[string]bool) - for _, path := range dstFiles { - pathgz := path - if filepath.Ext(path) == ".gz" { - path = path[:len(path)-len(".gz")] - } else { - pathgz = pathgz + ".gz" - } - _, name := filepath.Split(path) - present[name] = true - _, namegz := filepath.Split(pathgz) - present[namegz] = true + _, name := filepath.Split(path) + present[name] = true + _, namegz := filepath.Split(pathgz) + present[namegz] = true + } + dups := 0 + nodups := make([]string, 0) + for _, path := range srcFiles { + _, name := filepath.Split(path) + if ok, _ := present[name]; !ok { + nodups = append(nodups, path) + } else { + dups++ } - files := make([]string, 0) - for _, path := range srcFiles { - _, name := filepath.Split(path) - if ok, _ := present[name]; !ok { - files = append(files, path) + } + // Skip EVT files that are before t.Earliest + early := 0 + files := make([]string, 0) + for _, path := range nodups { + if !t.Earliest.IsZero() { + filetime, err := timeFromFilename(path) + if err == nil && filetime.Before(t.Earliest) { + t.Info.Printf("skipping %v: %v < %v\n", path, filetime, t.Earliest) + early++ + continue } + // otherwise default to transferring files that don't have parseable + // timestamps or are not before t.Earliest } + files = append(files, path) + } - t.Info.Printf("skipped %v duplicates and the most recent EVT file\n", len(srcFiles)-len(files)) - // Copy files - for _, path := range files { - err := t.CopyFile(path, true) - if err != nil { - return fmt.Errorf("error while copying %v: %v", path, err) - } - t.Info.Printf("copied %v\n", path) + t.Info.Printf("skipped %v duplicates\n", dups) + if !t.Earliest.IsZero() { + t.Info.Printf("skipped %v EVT files earlier than %v\n", early, t.Earliest) + } + t.Info.Printf("skipped the most recent EVT file\n") + + // Copy files + for _, path := range files { + err := t.CopyFile(path, true) + if err != nil { + return fmt.Errorf("error while copying %v: %v", path, err) } + t.Info.Printf("copied %v\n", path) } + return nil } @@ -361,3 +401,16 @@ func newSftpClient(addr string, user string, pass string, publickey string) (cli } return client, nil } + +// timeFromFilename parses a SeaFlow timestamped filename. This function assumes +// all times are UTC, even if they have non-UTC timezone designator. +func timeFromFilename(fn string) (time.Time, error) { + fnbase := filepath.Base(fn) + re := regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2})-(\d{2})-(\d{2}(?:\.\d+)?)(?:.+)?$`) + subs := re.FindStringSubmatch(fnbase) + if len(subs) != 4 { + return time.Time{}, fmt.Errorf("file timtestamp could not be parsed for %v", fn) + } + ts := subs[1] + ":" + subs[2] + ":" + subs[3] + "Z" + return time.Parse(time.RFC3339, ts) +} diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go index 798c58b..2492d4b 100644 --- a/internal/fs/fs_test.go +++ b/internal/fs/fs_test.go @@ -72,7 +72,7 @@ func testCopyFile(suite *StorageTestSuite) { return } assert.FileExists(filepath.Join(suite.dstDir, a), a+" copied") - assert.Equal("a", readFile(filepath.Join(suite.dstDir, a)), a+" contents are correct") + assert.Equal("a", readFile(filepath.Join(suite.dstDir, a)), a+" content is correct") assert.Equal( mtime(filepath.Join(suite.srcDir, a)).UnixNano()/nanoseconds, mtime(filepath.Join(suite.dstDir, a)).UnixNano()/nanoseconds, @@ -97,7 +97,7 @@ func testCopyFilegz(suite *StorageTestSuite) { return } assert.FileExists(filepath.Join(suite.dstDir, a+".gz"), a+" copied") - assert.Equal("a", readFilegz(filepath.Join(suite.dstDir, a+".gz")), a+" contents are correct") + assert.Equal("a", readFilegz(filepath.Join(suite.dstDir, a+".gz")), a+" content is correct") assert.Equal( mtime(filepath.Join(suite.srcDir, a)).UnixNano(), mtime(filepath.Join(suite.dstDir, a+".gz")).UnixNano(), @@ -110,7 +110,7 @@ func testCopyFilegz(suite *StorageTestSuite) { ) } -func (suite *StorageTestSuite) TestCopyFileAlreadygzLocaLocal() { +func (suite *StorageTestSuite) TestCopyFileAlreadygzLocalLocal() { testCopyFileAlreadygz(suite) } @@ -128,14 +128,14 @@ func testCopyFileAlreadygz(suite *StorageTestSuite) { return } assert.FileExists(filepath.Join(suite.dstDir, a), a+" copied") - assert.Equal("a", readFilegz(filepath.Join(suite.dstDir, a)), a+" contents are correct") + assert.Equal("a", readFilegz(filepath.Join(suite.dstDir, a)), a+" content is correct") assert.True( mtime(filepath.Join(suite.srcDir, a)).Equal(mtime(filepath.Join(suite.dstDir, a))), a+" modtime updated", ) } -func (suite *StorageTestSuite) TestCopySFLFilesNoMatchesLocaLocal() { +func (suite *StorageTestSuite) TestCopySFLFilesNoMatchesLocalLocal() { testCopySFLFilesNoMatches(suite) } func testCopySFLFilesNoMatches(suite *StorageTestSuite) { @@ -148,7 +148,7 @@ func testCopySFLFilesNoMatches(suite *StorageTestSuite) { assert.True(dirNotExists(suite.dstDir), "dest directory not created") } -func (suite *StorageTestSuite) TestCopyEVTFilesNoMatchesLocaLocal() { +func (suite *StorageTestSuite) TestCopyEVTFilesNoMatchesLocalLocal() { testCopyEVTFilesNoMatches(suite) } @@ -183,8 +183,8 @@ func testCopySFLFiles(suite *StorageTestSuite) { } assert.FileExists(filepath.Join(suite.dstDir, a), a+" copied") assert.FileExists(filepath.Join(suite.dstDir, b), b+" copied") - assert.Equal("a", readFile(filepath.Join(suite.dstDir, a)), a+" contents are correct") - assert.Equal("b", readFile(filepath.Join(suite.dstDir, b)), b+" contents are correct") + assert.Equal("a", readFile(filepath.Join(suite.dstDir, a)), a+" content is correct") + assert.Equal("b", readFile(filepath.Join(suite.dstDir, b)), b+" content is correct") // Change source files makeFile(filepath.Join(suite.srcDir, a), "aa") @@ -196,11 +196,59 @@ func testCopySFLFiles(suite *StorageTestSuite) { if err != nil { return } - assert.Equal("aa", readFile(filepath.Join(suite.dstDir, a)), a+" contents are correct") - assert.Equal("bb", readFile(filepath.Join(suite.dstDir, b)), b+" contents are correct") + assert.Equal("aa", readFile(filepath.Join(suite.dstDir, a)), a+" content is correct") + assert.Equal("bb", readFile(filepath.Join(suite.dstDir, b)), b+" content is correct") } -func (suite *StorageTestSuite) testCopyEVTFilesLocalLocal() { +func (suite *StorageTestSuite) TestCopySFLFilesWithTimeLocalLocal() { + testCopySFLFilesWithTime(suite) +} + +func testCopySFLFilesWithTime(suite *StorageTestSuite) { + assert := assert.New(suite.T()) + suite.t.Earliest, _ = time.Parse(time.RFC3339, "2016-05-12T04:00:00Z") + a := filepath.Join("2016_133", "a.sfl") + b := filepath.Join("2016_133", "2016-05-12T03-00-00-00-00.sfl") // early file, should not get copied + c := filepath.Join("2016_133", "2016-05-12T04-00-00-00-00.sfl") + d := filepath.Join("2016_133", "2016-05-12T05-00-00-00-00.sfl") + mkdir(filepath.Join(suite.srcDir, "2016_133")) + makeFile(filepath.Join(suite.srcDir, a), "a") + makeFile(filepath.Join(suite.srcDir, b), "b") + makeFile(filepath.Join(suite.srcDir, c), "c") + makeFile(filepath.Join(suite.srcDir, d), "d") + + err := suite.t.CopySFLFiles() + + assert.Nil(err) + if err != nil { + return + } + assert.FileExists(filepath.Join(suite.dstDir, a), a+" copied") + assert.True(fileNotExists(filepath.Join(suite.dstDir, b)), b+" early file not copied") + assert.FileExists(filepath.Join(suite.dstDir, c), c+" copied") + assert.FileExists(filepath.Join(suite.dstDir, d), d+" copied") + assert.Equal("a", readFile(filepath.Join(suite.dstDir, a)), a+" content is correct") + assert.Equal("c", readFile(filepath.Join(suite.dstDir, c)), c+" content is correct") + assert.Equal("d", readFile(filepath.Join(suite.dstDir, d)), d+" content is correct") + + // Change source files + makeFile(filepath.Join(suite.srcDir, a), "aa") + makeFile(filepath.Join(suite.srcDir, c), "cc") + makeFile(filepath.Join(suite.srcDir, d), "dd") + + err = suite.t.CopySFLFiles() + + assert.Nil(err) + if err != nil { + return + } + assert.Equal("aa", readFile(filepath.Join(suite.dstDir, a)), a+" content is correct") + assert.True(fileNotExists(filepath.Join(suite.dstDir, b)), b+" early file not copied") + assert.Equal("cc", readFile(filepath.Join(suite.dstDir, c)), c+" content is correct") + assert.Equal("dd", readFile(filepath.Join(suite.dstDir, d)), d+" content is correct") +} + +func (suite *StorageTestSuite) TestCopyEVTFilesLocalLocal() { testCopyEVTFiles(suite) } @@ -227,12 +275,12 @@ func testCopyEVTFiles(suite *StorageTestSuite) { assert.Equal( "a", readFilegz(filepath.Join(suite.dstDir, a+".gz")), - a+" contents are correct and file was not gzipped (again) in transit", + a+" content is correct and file was not gzipped (again) in transit", ) assert.Equal( "b", readFilegz(filepath.Join(suite.dstDir, b+".gz")), - b+" contents are correct and have been gzipped in transit", + b+" content is correct and have been gzipped in transit", ) // Change source files @@ -249,6 +297,68 @@ func testCopyEVTFiles(suite *StorageTestSuite) { assert.Equal("b", readFilegz(filepath.Join(suite.dstDir, b+".gz")), b+" content was not updated because it already exists") } +func (suite *StorageTestSuite) TestCopyEVTFilesWithTimeLocalLocal() { + testCopyEVTFilesWithTime(suite) +} + +func testCopyEVTFilesWithTime(suite *StorageTestSuite) { + assert := assert.New(suite.T()) + suite.t.Earliest, _ = time.Parse(time.RFC3339, "2016-05-12T04:00:00Z") + a := filepath.Join("2016_133", "2016-05-12T03-00-02-00-00") // early file, should not get copied + b := filepath.Join("2016_133", "2016-05-12T04-00-05-00-00") + c := filepath.Join("2016_133", "2016-05-12T05-00-05-00-00") + d := filepath.Join("2016_133", "2016-05-12T06-00-05-00-00") // last file, should not get copied + mkdir(filepath.Join(suite.srcDir, "2016_133")) + makeFile(filepath.Join(suite.srcDir, a), "a") + makeFile(filepath.Join(suite.srcDir, b), "b") + makeFile(filepath.Join(suite.srcDir, c), "c") + makeFile(filepath.Join(suite.srcDir, d), "d") + + err := suite.t.CopyEVTFiles() + + assert.Nil(err) + if err != nil { + return + } + assert.True(fileNotExists(filepath.Join(suite.dstDir, a+".gz")), a+" early file not copied") + assert.FileExists(filepath.Join(suite.dstDir, b+".gz"), b+" copied") + assert.FileExists(filepath.Join(suite.dstDir, c+".gz"), c+" copied") + assert.True(fileNotExists(filepath.Join(suite.dstDir, d+".gz")), d+" last file not copied") + assert.Equal( + "b", + readFilegz(filepath.Join(suite.dstDir, b+".gz")), + b+" content is correct and have been gzipped in transit", + ) + assert.Equal( + "c", + readFilegz(filepath.Join(suite.dstDir, c+".gz")), + c+" content is correct and have been gzipped in transit", + ) + + // Change source files + makeFile(filepath.Join(suite.srcDir, b), "bb") + makeFile(filepath.Join(suite.srcDir, c), "cc") + + err = suite.t.CopyEVTFiles() + + assert.Nil(err) + if err != nil { + return + } + assert.True(fileNotExists(filepath.Join(suite.dstDir, a+".gz")), a+" early file not copied") + assert.Equal( + "b", + readFilegz(filepath.Join(suite.dstDir, b+".gz")), + b+" content was not updated because it already exists", + ) + assert.Equal( + "c", + readFilegz(filepath.Join(suite.dstDir, c+".gz")), + c+" content was not updated because it already exists", + ) + assert.True(fileNotExists(filepath.Join(suite.dstDir, d+".gz")), d+" last file not copied") +} + func chtimes(path string, atime time.Time, mtime time.Time) { err := os.Chtimes(path, atime, mtime) if err != nil { @@ -356,3 +466,81 @@ func readFilegz(path string) string { } return string(data[:n]) } + +func Test_timeFromFilename(t *testing.T) { + timeAnswer, _ := time.Parse(time.RFC3339, "2019-12-06T22:58:10Z") + timeAnswerFrac, _ := time.Parse(time.RFC3339, "2019-12-06T22:58:10.3Z") + type args struct { + fn string + } + tests := []struct { + name string + args args + want time.Time + wantErr bool + }{ + { + name: "correct with no gz", + args: args{fn: "2019-12-06T22-58-10+00-00"}, + want: timeAnswer, + wantErr: false, + }, + { + name: "correct SFL", + args: args{fn: "2019-12-06T22-58-10+00-00.sfl"}, + want: timeAnswer, + wantErr: false, + }, + { + name: "correct with gz", + args: args{fn: "2019-12-06T22-58-10+00-00.gz"}, + want: timeAnswer, + wantErr: false, + }, + { + name: "correct with folders", + args: args{fn: "some/directory/path/2019-12-06T22-58-10+00-00"}, + want: timeAnswer, + wantErr: false, + }, + { + name: "correct with non-UTC TZ", + args: args{fn: "2019-12-06T22-58-10+07-00"}, + want: timeAnswer, + wantErr: false, + }, + { + name: "correct with fractional seconds", + args: args{fn: "2019-12-06T22-58-10.3+00-00"}, + want: timeAnswerFrac, + wantErr: false, + }, + { + name: "incorrect with no gz", + args: args{fn: "2019-12-06Ta22-58-10+00-00"}, + wantErr: true, + }, + { + name: "incorrect with gz", + args: args{fn: "2019-12-06Ta22-58-10+00-00.gz"}, + wantErr: true, + }, + { + name: "incorrect with folders", + args: args{fn: "some/directory/path/201a9-12-06T22-58-10+00-00"}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := timeFromFilename(tt.args.fn) + if (err != nil) != tt.wantErr { + t.Errorf("timeFromFilename() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !got.Equal(tt.want) { + t.Errorf("timeFromFilename() = %v, want %v", got, tt.want) + } + }) + } +}