From f73c352aeda9501c067a5f2a7566e3caeda4264a Mon Sep 17 00:00:00 2001 From: Luther Monson Date: Sun, 24 Dec 2023 18:54:26 -0700 Subject: [PATCH] adding list tests, reworked erroring to be testable --- cmd/{main.go => app.go} | 60 +++++++++++++++++++++++++++++++++++++-- cmd/app_test.go | 36 ++++++++++++++++++++++++ cmd/list.go | 2 +- cmd/list_test.go | 29 +++++++++++++++++++ mage/test/test.go | 2 +- magefile.go | 14 +++++++++- main.go | 62 ++++++----------------------------------- main_test.go | 40 ++++++++++++++++---------- 8 files changed, 171 insertions(+), 74 deletions(-) rename cmd/{main.go => app.go} (66%) create mode 100644 cmd/app_test.go create mode 100644 cmd/list_test.go diff --git a/cmd/main.go b/cmd/app.go similarity index 66% rename from cmd/main.go rename to cmd/app.go index 3e1c363..dbb0bfd 100644 --- a/cmd/main.go +++ b/cmd/app.go @@ -8,9 +8,61 @@ import ( "github.com/goodhosts/hostsfile" "github.com/olekukonko/tablewriter" "github.com/sirupsen/logrus" + easy "github.com/t-tomalak/logrus-easy-formatter" "github.com/urfave/cli/v2" ) +var ( + version string + App = &cli.App{ + Name: "goodhosts", + Usage: "manage your hosts file goodly", + Action: DefaultAction, + Commands: append(Commands(), &cli.Command{ + Name: "version", + Usage: "", + Aliases: []string{"v", "ver"}, + Action: func(c *cli.Context) error { + logrus.Infof(version) + return nil + }, + }), + Before: func(ctx *cli.Context) error { + if ctx.Bool("debug") { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetFormatter(&logrus.TextFormatter{}) + } else { + // treat logrus like fmt.Print + logrus.SetFormatter(&easy.Formatter{ + LogFormat: "%msg%", + }) + } + if ctx.Bool("quiet") { + logrus.SetOutput(io.Discard) + } + return nil + }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "file", + Aliases: []string{"f"}, + Value: "", + Usage: fmt.Sprintf("override the default hosts: %s", hostsfile.HostsFilePath), + }, + &cli.BoolFlag{ + Name: "debug", + Aliases: []string{"d"}, + Usage: "Turn on verbose debug logging", + }, + &cli.BoolFlag{ + Name: "quiet", + Aliases: []string{"q"}, + Usage: "Turn on off all logging", + }, + }, + } +) + func Commands() []*cli.Command { return []*cli.Command{ Add(), @@ -25,6 +77,10 @@ func Commands() []*cli.Command { } } +func Version(v string) { + version = v +} + func DefaultAction(c *cli.Context) error { return list(c) } @@ -43,11 +99,11 @@ func loadHostsfile(c *cli.Context, readOnly bool) (*hostsfile.Hosts, error) { } if err != nil { - return hf, cli.Exit(err, 1) + return hf, err } if !readOnly && !hf.IsWritable() { - return hf, cli.Exit("Host file not writable. Try running with elevated privileges.", 1) + return hf, fmt.Errorf("Hostsfile %s not writable. Try running with elevated privileges.", hf.Path) } return hf, nil diff --git a/cmd/app_test.go b/cmd/app_test.go new file mode 100644 index 0000000..95198bb --- /dev/null +++ b/cmd/app_test.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "bytes" + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/sirupsen/logrus" +) + +// Setup takes args slice and resets logrus and returns args to pass to App.Run +func setup(args ...string) ([]string, *bytes.Buffer) { + out := &bytes.Buffer{} + logrus.SetOutput(out) + a := os.Args[0:1] + a = append(a, args...) + return a, out +} + +func read(t *testing.T, name, file string) func() { + err := os.WriteFile(name, []byte(file), 0440) + assert.Nil(t, err) + return func() { + assert.Nil(t, os.Remove(name)) + } +} + +//func write(t *testing.T, name, file string) func() { +// err := os.WriteFile(name, []byte(file), 0770) +// assert.Nil(t, err) +// return func() { +// assert.Nil(t, os.Remove(name)) +// } +//} diff --git a/cmd/list.go b/cmd/list.go index ce45bfa..5d8459b 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -21,7 +21,7 @@ func List() *cli.Command { } func list(c *cli.Context) error { - hf, err := loadHostsfile(c, false) + hf, err := loadHostsfile(c, true) if err != nil { return err } diff --git a/cmd/list_test.go b/cmd/list_test.go new file mode 100644 index 0000000..97b9e9e --- /dev/null +++ b/cmd/list_test.go @@ -0,0 +1,29 @@ +package cmd + +import ( + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestList(t *testing.T) { + args, _ := setup("-f", "no-existy", "list") + err := App.Run(args) + assert.NotEmpty(t, err) + if runtime.GOOS == "windows" { + assert.Equal(t, "open no-existy: The system cannot find the file specified.", err.Error()) + } else { + assert.Equal(t, "open no-existy: no such file or directory", err.Error()) + } + + file := "list" + content := "127.0.0.1 localhost" + + cleanup := read(t, file, content) + defer cleanup() + + args, out := setup("-f", file, "list") + assert.Empty(t, App.Run(args)) + assert.Equal(t, content+"\n", out.String()) +} diff --git a/mage/test/test.go b/mage/test/test.go index 6256b5e..f2235fd 100644 --- a/mage/test/test.go +++ b/mage/test/test.go @@ -25,7 +25,7 @@ func Build() error { // Coverage run all unit tests and output coverage func Coverage() error { fmt.Println("Running Tests with Coverage...") - return sh.RunV("go", "test", "-v", "-coverprofile=coverage.txt", ".") + return sh.RunV("go", "test", "-v", "-coverprofile=coverage.txt", "./...") } // HTML display the html coverage report from the cover tool diff --git a/magefile.go b/magefile.go index dec58fa..b6609c8 100644 --- a/magefile.go +++ b/magefile.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" @@ -33,11 +34,22 @@ func Ci() error { return nil } +// build goodhosts cli locally +func Build() error { + fmt.Println("Building goodhosts...") + out := "goodhosts" + if runtime.GOOS == "windows" { + out = "goodhosts.exe" + } + sh.RunV("go", "build", "-o", out, "main.go") + return nil +} + // run the linter func Lint() error { mg.Deps(install.Golangcilint) fmt.Println("Running Linter...") - return sh.RunV("./bin/golangci-lint", "run") + return sh.RunV("golangci-lint", "run") } // delete files and paths made by mage diff --git a/main.go b/main.go index 30d8415..a990080 100644 --- a/main.go +++ b/main.go @@ -2,14 +2,11 @@ package main import ( "fmt" - "io" "os" - "github.com/goodhosts/cli/cmd" - "github.com/goodhosts/hostsfile" "github.com/sirupsen/logrus" - easy "github.com/t-tomalak/logrus-easy-formatter" - "github.com/urfave/cli/v2" + + "github.com/goodhosts/cli/cmd" ) var ( @@ -18,60 +15,17 @@ var ( date = "unknown" ) -var app = &cli.App{ - Name: "goodhosts", - Usage: "manage your hosts file goodly", - Action: cmd.DefaultAction, - Commands: append(cmd.Commands(), &cli.Command{ - Name: "version", - Usage: "", - Aliases: []string{"v", "ver"}, - Action: func(c *cli.Context) error { - logrus.Infof("goodhosts %s@%s built on %s", version, commit, date) - return nil - }, - }), - Before: func(ctx *cli.Context) error { - if ctx.Bool("debug") { - logrus.SetLevel(logrus.DebugLevel) - logrus.SetFormatter(&logrus.TextFormatter{}) - } else { - // treat logrus like fmt.Print - logrus.SetFormatter(&easy.Formatter{ - LogFormat: "%msg%", - }) - } - if ctx.Bool("quiet") { - logrus.SetOutput(io.Discard) - } - return nil - }, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "file", - Aliases: []string{"f"}, - Value: "", - Usage: fmt.Sprintf("override the default hosts: %s", hostsfile.HostsFilePath), - }, - &cli.BoolFlag{ - Name: "debug", - Aliases: []string{"d"}, - Usage: "Turn on verbose debug logging", - }, - &cli.BoolFlag{ - Name: "quiet", - Aliases: []string{"q"}, - Usage: "Turn on off all logging", - }, - }, -} - func main() { if err := run(os.Args); err != nil { logrus.Fatal(err) } } +func formatVersion(version, commit, date string) string { + return fmt.Sprintf("goodhosts %s@%s built on %s", version, commit, date) +} + func run(args []string) error { - return app.Run(args) + cmd.Version(formatVersion(version, commit, date)) + return cmd.App.Run(args) } diff --git a/main_test.go b/main_test.go index 5c60b2a..d0774d2 100644 --- a/main_test.go +++ b/main_test.go @@ -6,36 +6,46 @@ import ( "strings" "testing" + "github.com/goodhosts/cli/cmd" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) -func TestVersion(t *testing.T) { +// Run exported in tests to let subcommands call the main run +// Setup takes args slice and resets logrus and returns args to pass to App.Run +func setup(args ...string) ([]string, *bytes.Buffer) { out := &bytes.Buffer{} logrus.SetOutput(out) + a := os.Args[0:1] + a = append(a, args...) + return a, out +} - args := os.Args[0:1] - args = append(args, "version") +func TestVersion(t *testing.T) { + // test version passed in run() + args, out := setup("version") assert.Nil(t, run(args)) assert.Equal(t, "goodhosts dev@none built on unknown", out.String()) + + // test that version@commit + date work + args, out = setup("version") + cmd.Version(formatVersion("test-version", "test-commit", "test-date")) + assert.Nil(t, cmd.App.Run(args)) + assert.Equal(t, "goodhosts test-version@test-commit built on test-date", out.String()) + + // reset for future tests + cmd.Version(formatVersion("dev", "none", "unknown")) } func TestDebug(t *testing.T) { - out := &bytes.Buffer{} - logrus.SetOutput(out) - - args := os.Args[0:1] - args = append(args, "--debug", "version") - assert.Nil(t, run(args)) + args, out := setup("--debug", "version") + assert.Nil(t, cmd.App.Run(args)) assert.True(t, strings.Contains(out.String(), "level=info msg=\"goodhosts dev@none built on unknown\"")) } func TestQuiet(t *testing.T) { - out := &bytes.Buffer{} - logrus.SetOutput(out) - - args := os.Args[0:1] - args = append(args, "--quiet", "version") - assert.Nil(t, run(args)) + args, out := setup("--quiet", "version") + assert.Nil(t, cmd.App.Run(args)) assert.Empty(t, out.String()) }