diff --git a/godfish.go b/godfish.go index dec89dc..2386975 100644 --- a/godfish.go +++ b/godfish.go @@ -162,19 +162,6 @@ func figureOutBasename(directoryPath string, direction internal.Direction, versi return } -type runMigrationError struct { - driverName string - originalError error - path string -} - -func (e *runMigrationError) Error() string { - return fmt.Sprintf( - "driver: %q, path: %q, error: %v", - e.driverName, e.path, e.originalError, - ) -} - // runMigration executes a migration against the database. The input, pathToFile // should be relative to the current working directory. func runMigration(driver Driver, pathToFile string, mig internal.Migration) (err error) { @@ -189,11 +176,7 @@ func runMigration(driver Driver, pathToFile string, mig internal.Migration) (err fmt.Fprintf(os.Stderr, "%s version %q ... ", gerund, mig.Version().String()) if err = driver.Execute(string(data)); err != nil { - err = &runMigrationError{ - driverName: driver.Name(), - originalError: err, - path: pathToFile, - } + err = fmt.Errorf("%w, path_to_file: %q", err, pathToFile) return } if err = driver.CreateSchemaMigrationsTable(); err != nil { diff --git a/godfish_test.go b/godfish_test.go index 777fc1f..14f1e2a 100644 --- a/godfish_test.go +++ b/godfish_test.go @@ -4,27 +4,134 @@ import ( "database/sql" "encoding/json" "os" + "strings" "testing" "github.com/rafaelespinoza/godfish" "github.com/rafaelespinoza/godfish/internal" + "github.com/rafaelespinoza/godfish/internal/stub" + "github.com/rafaelespinoza/godfish/internal/test" ) -const baseTestOutputDir = "/tmp/godfish_test" +const ( + baseTestOutputDir = "/tmp/godfish_test" + dsnKey = "DB_DSN" +) -func TestInit(t *testing.T) { - var err error - testOutputDir := baseTestOutputDir + "/" + t.Name() - if err = os.MkdirAll(testOutputDir, 0755); err != nil { +func makeTestDir(t *testing.T, basedir string) (outpath string) { + if basedir == "" { + basedir = os.TempDir() + } + err := os.MkdirAll(basedir, 0750) + if err != nil { t.Fatal(err) } - - var pathToFile string - if outdirPath, err := os.MkdirTemp(testOutputDir, ""); err != nil { + outpath, err = os.MkdirTemp(basedir, strings.Replace(t.Name(), "/", "_", -1)) + if err != nil { t.Fatal(err) - } else { - pathToFile = outdirPath + "/config.json" } + return +} + +func TestCreateMigrationFiles(t *testing.T) { + t.Run("err", func(t *testing.T) { + testdir := makeTestDir(t, baseTestOutputDir) + err := godfish.CreateMigrationFiles("err_test", true, testdir, "bad", "bad2") + if err == nil { + t.Fatal(err) + } + }) + + t.Run("ok", func(t *testing.T) { + testdir := makeTestDir(t, "") + err := godfish.CreateMigrationFiles("err_test", true, testdir, "", "") + if err != nil { + t.Fatal(err) + } + + entries, err := os.ReadDir(testdir) + if err != nil { + t.Fatal(err) + } + if len(entries) != 2 { + t.Fatalf("wrong number of entries, got %d, expected %d", len(entries), 2) + } + + for i, direction := range []string{"forward", "reverse"} { + got := entries[i].Name() + if !strings.HasPrefix(got, direction) { + t.Errorf("expected filename, %q, to have prefix %q", got, direction) + } + if !strings.HasSuffix(got, "err_test.sql") { + t.Errorf("expected filename, %q, to have suffix %q", got, "err_test.sql") + } + } + }) +} + +func TestMigrate(t *testing.T) { + t.Run("missing DB_DSN", func(t *testing.T) { + t.Setenv(dsnKey, "") + + testdir := makeTestDir(t, baseTestOutputDir) + err := godfish.Migrate(stub.NewDriver(), testdir, false, "") + if err == nil { + t.Fatalf("expected an error, got %v", err) + } + got := err.Error() + if !strings.Contains(got, dsnKey) { + t.Errorf("expected error message %q to mention %q", got, dsnKey) + } + }) +} + +func TestApplyMigration(t *testing.T) { + t.Run("missing DB_DSN", func(t *testing.T) { + t.Setenv(dsnKey, "") + + testdir := makeTestDir(t, baseTestOutputDir) + err := godfish.ApplyMigration(stub.NewDriver(), testdir, false, "") + if err == nil { + t.Fatalf("expected an error, got %v", err) + } + got := err.Error() + if !strings.Contains(got, dsnKey) { + t.Errorf("expected error message %q to mention %q", got, dsnKey) + } + }) +} + +func TestInfo(t *testing.T) { + t.Run("missing DB_DSN", func(t *testing.T) { + t.Setenv(dsnKey, "") + + testdir := makeTestDir(t, baseTestOutputDir) + err := godfish.Info(stub.NewDriver(), testdir, false, "", os.Stderr, "") + if err == nil { + t.Fatalf("expected an error, got %v", err) + } + got := err.Error() + if !strings.Contains(got, dsnKey) { + t.Errorf("expected error message %q to mention %q", got, dsnKey) + } + }) + + t.Run("unknown format does not error out", func(t *testing.T) { + t.Setenv(dsnKey, "test") + + testdir := makeTestDir(t, baseTestOutputDir) + err := godfish.Info(stub.NewDriver(), testdir, false, "", os.Stderr, "tea_ess_vee") + if err != nil { + t.Fatalf("unexpected error, %v", err) + } + }) +} + +func TestInit(t *testing.T) { + var err error + testOutputDir := makeTestDir(t, "") + + pathToFile := testOutputDir + "/config.json" // setup: file should not exist at first if _, err = os.Stat(pathToFile); !os.IsNotExist(err) { @@ -78,3 +185,10 @@ func TestAppliedVersions(t *testing.T) { t.Fatalf("expected %T to implement godfish.AppliedVersions", thing) } } + +func TestDriver(t *testing.T) { + // These tests also run in the stub package. They are duplicated here to + // make test coverage tool consider the tests in godfish.go as covered. + t.Setenv("DB_DSN", "stub_dsn") + test.RunDriverTests(t, stub.NewDriver(), test.DefaultQueries) +} diff --git a/internal/filename_test.go b/internal/filename_test.go index 8c2c91d..c721877 100644 --- a/internal/filename_test.go +++ b/internal/filename_test.go @@ -1,77 +1,79 @@ -package internal +package internal_test import ( "testing" + + "github.com/rafaelespinoza/godfish/internal" ) func TestFilename(t *testing.T) { tests := []struct { version string - direction Indirection + direction internal.Indirection label string - expOut Filename + expOut internal.Filename }{ { version: "20191118121314", - direction: Indirection{Value: DirForward, Label: "forward"}, + direction: internal.Indirection{Value: internal.DirForward, Label: "forward"}, label: "test", - expOut: Filename("forward-20191118121314-test.sql"), + expOut: internal.Filename("forward-20191118121314-test.sql"), }, { version: "20191118121314", - direction: Indirection{Value: DirReverse, Label: "reverse"}, + direction: internal.Indirection{Value: internal.DirReverse, Label: "reverse"}, label: "test", - expOut: Filename("reverse-20191118121314-test.sql"), + expOut: internal.Filename("reverse-20191118121314-test.sql"), }, // timestamp too long { version: "201911181213141516", - direction: Indirection{Value: DirForward, Label: "forward"}, + direction: internal.Indirection{Value: internal.DirForward, Label: "forward"}, label: "test", - expOut: Filename("forward-20191118121314-test.sql"), + expOut: internal.Filename("forward-20191118121314-test.sql"), }, // timestamp too short { version: "1234", - direction: Indirection{Value: DirForward, Label: "forward"}, + direction: internal.Indirection{Value: internal.DirForward, Label: "forward"}, label: "test", - expOut: Filename("forward-1234-test.sql"), + expOut: internal.Filename("forward-1234-test.sql"), }, // label has dashes { version: "20191118121314", - direction: Indirection{Value: DirForward, Label: "forward"}, + direction: internal.Indirection{Value: internal.DirForward, Label: "forward"}, label: "foo-bar", - expOut: Filename("forward-20191118121314-foo-bar.sql"), + expOut: internal.Filename("forward-20191118121314-foo-bar.sql"), }, // alternative names { - direction: Indirection{Value: DirForward, Label: "migrate"}, + direction: internal.Indirection{Value: internal.DirForward, Label: "migrate"}, version: "20191118121314", label: "test", - expOut: Filename("migrate-20191118121314-test.sql"), + expOut: internal.Filename("migrate-20191118121314-test.sql"), }, { - direction: Indirection{Value: DirForward, Label: "up"}, + direction: internal.Indirection{Value: internal.DirForward, Label: "up"}, version: "20191118121314", label: "test", - expOut: Filename("up-20191118121314-test.sql"), + expOut: internal.Filename("up-20191118121314-test.sql"), }, { - direction: Indirection{Value: DirReverse, Label: "rollback"}, + direction: internal.Indirection{Value: internal.DirReverse, Label: "rollback"}, version: "20191118121314", label: "test", - expOut: Filename("rollback-20191118121314-test.sql"), + expOut: internal.Filename("rollback-20191118121314-test.sql"), }, { - direction: Indirection{Value: DirReverse, Label: "down"}, + direction: internal.Indirection{Value: internal.DirReverse, Label: "down"}, version: "20191118121314", label: "test", - expOut: Filename("down-20191118121314-test.sql"), + expOut: internal.Filename("down-20191118121314-test.sql"), }, } for i, test := range tests { - out := MakeFilename(test.version, test.direction, test.label) + out := internal.MakeFilename(test.version, test.direction, test.label) if out != string(test.expOut) { t.Errorf( "test %d; wrong filename; got %q, expected %q", @@ -80,115 +82,3 @@ func TestFilename(t *testing.T) { } } } - -func mustMakeMigration(version string, indirection Indirection, label string) Migration { - ver, err := ParseVersion(version) - if err != nil { - panic(err) - } - return &mutation{ - indirection: indirection, - label: label, - version: ver, - } -} - -func TestParseMigration(t *testing.T) { - tests := []struct { - filename Filename - expOut Migration - expErr bool - }{ - { - filename: Filename("forward-20191118121314-test.sql"), - expOut: mustMakeMigration( - "20191118121314", - Indirection{Value: DirForward, Label: "forward"}, - "test", - ), - }, - { - filename: Filename("reverse-20191118121314-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirReverse}, "test"), - }, - // no extension - { - filename: Filename("forward-20191118121314-test"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirForward}, "test"), - }, - // timestamp too long - { - filename: Filename("forward-201911181213141516-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirForward}, "516-test"), - }, - // timestamp short - { - filename: Filename("forward-1234-test.sql"), - expOut: mustMakeMigration("1234", Indirection{Value: DirForward}, "test"), - }, - // unknown direction - {filename: Filename("foo-20191118121314-bar.sql"), expErr: true}, - // just bad - {filename: Filename("foo-bar"), expErr: true}, - // label has a delimiter - { - filename: Filename("forward-20191118121314-foo-bar.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirForward}, "foo-bar"), - }, - // alternative names for directions - { - filename: Filename("migrate-20191118121314-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirForward, Label: "migration"}, "test"), - }, - { - filename: Filename("up-20191118121314-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirForward, Label: "up"}, "test"), - }, - { - filename: Filename("rollback-20191118121314-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirReverse, Label: "rollback"}, "test"), - }, - { - filename: Filename("down-20191118121314-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirReverse, Label: "down"}, "test"), - }, - // unix timestamp (seconds) as version - { - filename: Filename("forward-1574079194-test.sql"), - expOut: mustMakeMigration("20191118121314", Indirection{Value: DirForward, Label: "forward"}, "test"), - }, - } - - for i, test := range tests { - actual, err := ParseMigration(test.filename) - if !test.expErr && err != nil { - t.Errorf("test %d; %v", i, err) - continue - } else if test.expErr && err == nil { - t.Errorf("test %d; expected error but did not get one", i) - continue - } else if test.expErr && err != nil { - continue // ok - } - if actual.Indirection().Value != test.expOut.Indirection().Value { - t.Errorf( - "test %d; wrong Direction; expected %s, got %s", - i, test.expOut.Indirection().Value, actual.Indirection().Value, - ) - } - if actual.Label() != test.expOut.Label() { - t.Errorf( - "test %d; wrong Name; expected %s, got %s", - i, test.expOut.Label(), actual.Label(), - ) - } - act := actual.Version() - exp := test.expOut.Version() - if act.Before(exp) || exp.Before(act) { - t.Errorf( - "test %d; wrong Timestamp; expected %s, got %s", - i, test.expOut.Version(), actual.Version(), - ) - } - } -} diff --git a/internal/info_test.go b/internal/info_test.go index 344e7e7..c20ebcb 100644 --- a/internal/info_test.go +++ b/internal/info_test.go @@ -118,7 +118,10 @@ func mustMakeMigrations(names ...string) []internal.Migration { if err != nil { panic(err) } - version := stub.NewVersion(strconv.Itoa((i + 1) * 1000)) + version, err := internal.ParseVersion(strconv.Itoa((i + 1) * 1000)) + if err != nil { + panic(err) + } out[i] = stub.NewMigration(params.Forward, version, internal.Indirection{}) } return out diff --git a/internal/migration_test.go b/internal/migration_test.go index 0d422ed..2b64a48 100644 --- a/internal/migration_test.go +++ b/internal/migration_test.go @@ -1,6 +1,7 @@ package internal_test import ( + "errors" "fmt" "os" "path/filepath" @@ -12,6 +13,162 @@ import ( const baseTestOutputDir = "/tmp/godfish_test" +func TestParseMigration(t *testing.T) { + type testCase struct { + filename internal.Filename + expErr bool + expIndirection internal.Indirection + expLabel string + // expVersionInput should be an input to ParseVersion, the + // implementation used in the test assertions is not exported, so you + // cannot directly construct it. + expVersionInput string + } + + runTest := func(t *testing.T, test testCase) { + actual, err := internal.ParseMigration(test.filename) + if !test.expErr && err != nil { + t.Fatal(err) + } else if test.expErr && err == nil { + t.Fatal("expected error but did not get one") + } else if test.expErr && err != nil { + // some unexported funcs in the godfish package have behavior that + // depend on this wrapped error. + if !errors.Is(err, internal.ErrDataInvalid) { + t.Fatalf("expected error %v to wrap %v", err, internal.ErrDataInvalid) + } + return // ok, nothing more to test. + } + + if actual.Indirection().Value != test.expIndirection.Value { + t.Errorf( + "wrong Direction; got %s, expected %s", + actual.Indirection().Value, test.expIndirection.Value, + ) + } + + if actual.Label() != test.expLabel { + t.Errorf( + "wrong Name; got %s, expected %s", + actual.Label(), test.expLabel, + ) + } + + expVersion, err := internal.ParseVersion(test.expVersionInput) + if err != nil { + t.Fatal(err) + } + gotVersion := actual.Version() + if gotVersion.Before(expVersion) || expVersion.Before(gotVersion) { + t.Errorf( + "wrong Version timestamp; got %s, expected %s", + gotVersion.String(), expVersion.String(), + ) + } + } + + t.Run("ok", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("forward-20191118121314-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward, Label: "forward"}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + + runTest(t, testCase{ + filename: internal.Filename("reverse-20191118121314-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirReverse}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + }) + + t.Run("no extension", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("forward-20191118121314-test"), + expIndirection: internal.Indirection{Value: internal.DirForward}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + }) + + t.Run("timestamp is too long", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("forward-201911181213141516-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward}, + expLabel: "516-test", + expVersionInput: "20191118121314", + }) + }) + + t.Run("timestamp is short", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("forward-1234-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward}, + expLabel: "test", + expVersionInput: "1234", + }) + + runTest(t, testCase{filename: internal.Filename("forward-123-test.sql"), expErr: true}) + }) + + t.Run("unknown direction", func(t *testing.T) { + runTest(t, testCase{filename: internal.Filename("foo-20191118121314-bar.sql"), expErr: true}) + }) + + t.Run("just bad", func(t *testing.T) { + runTest(t, testCase{filename: internal.Filename("foo-bar"), expErr: true}) + }) + + t.Run("label has a delimiter", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("forward-20191118121314-foo-bar.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward}, + expLabel: "foo-bar", + expVersionInput: "20191118121314", + }) + }) + + t.Run("alternative names for directions", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("migrate-20191118121314-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward, Label: "migration"}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + + runTest(t, testCase{ + filename: internal.Filename("up-20191118121314-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward, Label: "up"}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + + runTest(t, testCase{ + filename: internal.Filename("rollback-20191118121314-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirReverse, Label: "rollback"}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + + runTest(t, testCase{ + filename: internal.Filename("down-20191118121314-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirReverse, Label: "down"}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + }) + + t.Run("unix timestamps as version", func(t *testing.T) { + runTest(t, testCase{ + filename: internal.Filename("forward-1574079194-test.sql"), + expIndirection: internal.Indirection{Value: internal.DirForward, Label: "forward"}, + expLabel: "test", + expVersionInput: "20191118121314", + }) + }) +} + func TestMigrationParams(t *testing.T) { testOutputDir := baseTestOutputDir + "/" + t.Name() if err := os.MkdirAll(testOutputDir, 0755); err != nil { diff --git a/internal/stub/driver.go b/internal/stub/driver.go index d9f4cee..29c3541 100644 --- a/internal/stub/driver.go +++ b/internal/stub/driver.go @@ -10,28 +10,26 @@ import ( type driver struct { appliedVersions godfish.AppliedVersions - err error - errorOnExecute error } func NewDriver() godfish.Driver { return &driver{} } func (d *driver) Name() string { return "stub" } -func (d *driver) Connect(dsn string) error { return d.err } -func (d *driver) Close() error { return d.err } +func (d *driver) Connect(dsn string) error { return nil } +func (d *driver) Close() error { return nil } func (d *driver) CreateSchemaMigrationsTable() error { if d.appliedVersions == nil { d.appliedVersions = NewAppliedVersions() } - return d.err + return nil } func (d *driver) Execute(q string, a ...interface{}) error { if strings.Contains(q, "invalid SQL") { return fmt.Errorf(q) } - return d.errorOnExecute + return nil } func (d *driver) UpdateSchemaMigrations(forward bool, version string) error { @@ -70,7 +68,7 @@ func (d *driver) AppliedVersions() (godfish.AppliedVersions, error) { if d.appliedVersions == nil { return nil, godfish.ErrSchemaMigrationsDoesNotExist } - return d.appliedVersions, d.err + return d.appliedVersions, nil } // Teardown resets the stub driver in tests. All other Driver implementations diff --git a/internal/stub/driver_test.go b/internal/stub/driver_test.go index 9e50535..966adfe 100644 --- a/internal/stub/driver_test.go +++ b/internal/stub/driver_test.go @@ -8,5 +8,6 @@ import ( ) func Test(t *testing.T) { + t.Setenv("DB_DSN", "stub_dsn") test.RunDriverTests(t, stub.NewDriver(), test.DefaultQueries) } diff --git a/internal/stub/version.go b/internal/stub/version.go deleted file mode 100644 index 39e1746..0000000 --- a/internal/stub/version.go +++ /dev/null @@ -1,27 +0,0 @@ -package stub - -import ( - "strconv" - - "github.com/rafaelespinoza/godfish/internal" -) - -type version string - -// NewVersion converts the input to a Version for testing purposes. -func NewVersion(v string) internal.Version { return version(v) } - -func (v version) Before(u internal.Version) bool { - w := u.(version) // potential panic intended, keep tests simple - return string(v) < string(w) -} - -func (v version) String() string { return string(v) } - -func (v version) Value() int64 { - i, e := strconv.ParseInt(v.String()[:4], 10, 64) - if e != nil { - panic(e) - } - return i -} diff --git a/internal/test/test.go b/internal/test/test.go index 42057d9..99fa265 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -112,7 +112,13 @@ func teardown(driver godfish.Driver, path string, tablesToDrop ...string) { driver.Close() } -func formattedTime(v string) internal.Version { return stub.NewVersion(v) } +func formattedTime(v string) internal.Version { + out, err := internal.ParseVersion(v) + if err != nil { + panic(err) + } + return out +} // testDriverStub encompasses some data to use with interface tests. type testDriverStub struct { diff --git a/internal/version.go b/internal/version.go index e35e633..230c390 100644 --- a/internal/version.go +++ b/internal/version.go @@ -1,6 +1,7 @@ package internal import ( + "errors" "regexp" "strconv" "time" @@ -52,6 +53,10 @@ var timeformatMatcher = regexp.MustCompile(`\d{4,14}`) // ParseVersion extracts Version info from a file's basename. func ParseVersion(basename string) (version Version, err error) { written := timeformatMatcher.FindString(basename) + if len(written) < 1 { + err = errors.New("could not parse version from filename") + return + } if ts, perr := time.Parse(TimeFormat, written); perr != nil { err = perr // keep going } else { diff --git a/internal/version_test.go b/internal/version_test.go new file mode 100644 index 0000000..1767fc5 --- /dev/null +++ b/internal/version_test.go @@ -0,0 +1,65 @@ +package internal_test + +import ( + "testing" + "time" + + "github.com/rafaelespinoza/godfish/internal" +) + +func TestParseVersion(t *testing.T) { + type testCase struct { + input string + expVal int64 + expErr bool + } + + runTest := func(t *testing.T, test testCase) { + got, err := internal.ParseVersion(test.input) + if !test.expErr && err != nil { + t.Fatal(err) + } else if test.expErr && err == nil { + t.Fatal("expected error but did not get one") + } else if test.expErr && err != nil { + return // ok, nothing more to test. + } + + val := got.Value() + if val != test.expVal { + t.Errorf("wrong Value; got %d, expected %d", val, test.expVal) + } + } + + t.Run("regular timestamp layout", func(t *testing.T) { + runTest(t, testCase{ + input: "20060102030405", + expVal: time.Date(2006, time.January, 2, 3, 4, 5, 0, time.UTC).Unix(), + }) + }) + + t.Run("timestamp is short", func(t *testing.T) { + runTest(t, testCase{ + input: "1234", + expVal: time.Date(1234, time.January, 1, 0, 0, 0, 0, time.UTC).Unix(), + }) + + }) + + t.Run("timestamp is too short", func(t *testing.T) { + runTest(t, testCase{input: "123", expErr: true}) + }) + + t.Run("timestamp is long", func(t *testing.T) { + runTest(t, testCase{ + input: "2006010203040567890", + expVal: time.Date(2006, time.January, 2, 3, 4, 5, 0, time.UTC).Unix(), + }) + }) + + t.Run("unix timestamp", func(t *testing.T) { + runTest(t, testCase{ + input: "1574079194", + expVal: time.Date(2019, time.November, 18, 12, 13, 14, 0, time.UTC).Unix(), + }) + }) +}