diff --git a/.dccache b/.dccache new file mode 100644 index 0000000..7d2b83e --- /dev/null +++ b/.dccache @@ -0,0 +1 @@ +{"/Users/cs/GolandProjects/poke-cli/cli.go":[2908,1731199451826.8408,"fe32be489436462dc7bec15aff72fdaf8718d161897cd0c301a8500784c38dc6"],"/Users/cs/GolandProjects/poke-cli/cli_test.go":[5660,1731346247797.8955,"dc62994b7701b8f48827fa6cac2e5cbe2bc4f3618b00f1529e187934120c79aa"],"/Users/cs/GolandProjects/poke-cli/cmd/pokemon.go":[2025,1731107596626.2603,"34a4ca4a4ce21dbfca8ce0351171114f19ead62579a7bce12710271ce2b43b44"],"/Users/cs/GolandProjects/poke-cli/cmd/pokemon_test.go":[1564,1727734768286.3376,"e5349582c85c7edfed1ffec5dc2d1fb117ec123feaec97cfbee60f20b4eb08ab"],"/Users/cs/GolandProjects/poke-cli/cmd/styles.go":[1244,1729983248031.173,"8acc71493eb32a0cd65e0aa748277fe2c0b18510f2617276101d6909b6a5f2f4"],"/Users/cs/GolandProjects/poke-cli/cmd/types.go":[6577,1731092007241.011,"4203101e899b7c0648042f23b945e7f2689283ed9b2305dfb363cec01b4e57de"],"/Users/cs/GolandProjects/poke-cli/cmd/types_test.go":[718,1729532136457.2212,"fbc72c3a5cf4b1b01c294808f68311a69faf4def91af15f894fd418727b16f5d"],"/Users/cs/GolandProjects/poke-cli/cmd/validateargs.go":[2060,1731111394011.11,"9abf89d6b055b7be3de7f231181d22438262208daa816f35bf39577b0d9692cc"],"/Users/cs/GolandProjects/poke-cli/cmd/validateargs_test.go":[1652,1728245306004.284,"7da0fd36b2bd7fd0e9bce0bafc13f6a9620fe25d2fa810418dffe42b190280a9"],"/Users/cs/GolandProjects/poke-cli/connections/connection.go":[3107,1731346514110.1143,"cd23b8f48bc30086c13fa393aa3584be03740afa8fa972e062e1a44995c26067"],"/Users/cs/GolandProjects/poke-cli/connections/connection_test.go":[4293,1731345803130.106,"108799e7030af4359726d919459545e33e23e0021388e3215b8d5707c482fbe0"],"/Users/cs/GolandProjects/poke-cli/flags/pokemonflagset.go":[3298,1730065034797.2764,"21a4a30afe40eb9cbd5fd12999debbb05c183adcd32452150c5b0e34b54b832e"],"/Users/cs/GolandProjects/poke-cli/flags/pokemonflagset_test.go":[1257,1730225862960.686,"6be704fd558c2cfdebf0261de3ecd478ba887f128d630ec762a9f53f03f3a842"],"/Users/cs/GolandProjects/poke-cli/flags/version.go":[1345,1731111344374.6135,"ef6a00218a6ed9c380c04e310c7970eff0e0b707198278730cb225ff4614636b"],"/Users/cs/GolandProjects/poke-cli/flags/version_test.go":[2474,1731346090382.8333,"4e641a4c3216bdaf4a6d5fed4f06bc5770b9d4aeb3d9b1ab34150950617bcfbf"]} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5b5b1f..7605ebd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ on: branches: - main env: - VERSION_NUMBER: 'v0.7.0' + VERSION_NUMBER: 'v0.7.1' REGISTRY_NAME: digitalghostdev/poke-cli jobs: diff --git a/README.md b/README.md index 948c074..e726bb6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ pokemon-logo

Pokémon CLI

version-label - docker-image-size + docker-image-size @@ -16,7 +16,7 @@ A CLI tool for viewing data about Pokémon from your terminal! ## Demo -![demo](https://pokemon-objects.nyc3.digitaloceanspaces.com/demo.gif) +![demo](https://pokemon-objects.nyc3.digitaloceanspaces.com/demo_0.7.1.gif) ## Install @@ -40,7 +40,7 @@ _Taskfile can build the executable for you_ _Use a Docker Image_ ```bash -docker run --rm -it digitalghostdev/poke-cli:v0.7.0 [command] [subcommand] [flag] +docker run --rm -it digitalghostdev/poke-cli:v0.7.1 [command] [subcommand] [flag] ``` ### Go Build @@ -71,8 +71,8 @@ By running `poke-cli [-h | --help]`, it'll display information on how to use the │ │ │ USAGE: │ │ poke-cli [flag] │ -│ poke-cli [command] [flag] │ -│ poke-cli [command] [subcommand] [flag] │ +│ poke-cli [flag] │ +│ poke-cli [flag] │ │ │ │ FLAGS: │ │ -h, --help Shows the help menu │ diff --git a/cli.go b/cli.go index c4330d7..488447b 100644 --- a/cli.go +++ b/cli.go @@ -30,8 +30,8 @@ func runCLI(args []string) int { "Welcome! This tool displays data related to Pokémon!", "\n\n", styleBold.Render("USAGE:"), fmt.Sprintf("\n\t%-15s %s", "poke-cli [flag]", ""), - fmt.Sprintf("\n\t%-15s %s", "poke-cli [command] [flag]", ""), - fmt.Sprintf("\n\t%-15s %s", "poke-cli [command] [subcommand] [flag]", ""), + fmt.Sprintf("\n\t%-15s %s", "poke-cli [flag]", ""), + fmt.Sprintf("\n\t%-15s %s", "poke-cli [flag]", ""), "\n\n", styleBold.Render("FLAGS:"), fmt.Sprintf("\n\t%-15s %s", "-h, --help", "Shows the help menu"), fmt.Sprintf("\n\t%-15s %s", "-l, --latest", "Prints the latest available"), @@ -43,9 +43,12 @@ func runCLI(args []string) int { fmt.Println(helpMessage) } - // Check for help flag manually - for _, arg := range args { - if arg == "-h" || arg == "--help" { + switch { + case len(args) == 0: + mainFlagSet.Usage() + return 0 + case len(args) > 0: + if args[0] == "-h" || args[0] == "--help" { mainFlagSet.Usage() return 0 } @@ -71,8 +74,10 @@ func runCLI(args []string) int { cmdFunc() return 0 } else { + command := os.Args[1] errMessage := errorBorder.Render( errorColor.Render("Error!"), + fmt.Sprintf("\n\t%-15s", fmt.Sprintf("'%s' is not a valid command.\n", command)), styleBold.Render("\nAvailable Commands:"), fmt.Sprintf("\n\t%-15s %s", "pokemon", "Get details of a specific Pokémon"), fmt.Sprintf("\n\t%-15s %s", "types", "Get details of a specific typing\n"), @@ -83,7 +88,7 @@ func runCLI(args []string) int { } } -var exit = os.Exit // Default to os.Exit, but you can override this in tests +var exit = os.Exit func main() { exit(runCLI(os.Args[1:])) diff --git a/cli_test.go b/cli_test.go index a0d15d2..22241bb 100644 --- a/cli_test.go +++ b/cli_test.go @@ -17,7 +17,7 @@ func stripANSI(input string) string { } func TestMainFunction(t *testing.T) { - version := "v0.6.5" + version := "v0.7.0" // Backup the original exit function and stdout/stderr originalExit := exit @@ -42,6 +42,8 @@ func TestMainFunction(t *testing.T) { args: []string{"pokemons"}, expectedOutput: "╭──────────────────────────────────────────────────────╮\n" + "│Error! │\n" + + "│ 'pokemons' is not a valid command. │\n" + + "│ │\n" + "│Available Commands: │\n" + "│ pokemon Get details of a specific Pokémon │\n" + "│ types Get details of a specific typing │\n" + @@ -67,8 +69,8 @@ func TestMainFunction(t *testing.T) { "│ │\n" + "│ USAGE: │\n" + "│ poke-cli [flag] │\n" + - "│ poke-cli [command] [flag] │\n" + - "│ poke-cli [command] [subcommand] [flag] │\n" + + "│ poke-cli [flag] │\n" + + "│ poke-cli [flag] │\n" + "│ │\n" + "│ FLAGS: │\n" + "│ -h, --help Shows the help menu │\n" + diff --git a/cmd/pokemon.go b/cmd/pokemon.go index de58632..6996866 100644 --- a/cmd/pokemon.go +++ b/cmd/pokemon.go @@ -16,18 +16,15 @@ func PokemonCommand() { flag.Usage = func() { helpMessage := helpBorder.Render( + "Get details about a specific Pokémon.\n\n", styleBold.Render("USAGE:"), fmt.Sprintf("\n\t%s %s %s %s", "poke-cli", styleBold.Render("pokemon"), "", "[flag]"), - fmt.Sprintf("\n\t%-30s", "Get details about a specific Pokémon"), - fmt.Sprintf("\n\t%-30s", "----------"), - fmt.Sprintf("\n\t%-30s", styleItalic.Render("Examples:\n")), - fmt.Sprintf("\n\t%-30s", "poke-cli pokemon bulbasaur"), - fmt.Sprintf("\n\t%-30s", "poke-cli pokemon flutter-mane --types"), - fmt.Sprintf("\n\t%-30s", "poke-cli pokemon excadrill -t -a"), + fmt.Sprintf("\n\t%-30s", styleItalic.Render("Use a hyphen when typing a name with a space.")), "\n\n", styleBold.Render("FLAGS:"), fmt.Sprintf("\n\t%-30s %s", "-a, --abilities", "Prints out the Pokémon's abilities."), fmt.Sprintf("\n\t%-30s %s", "-t, --types", "Prints out the Pokémon's typing."), + fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints out the help menu."), ) fmt.Println(helpMessage) } diff --git a/cmd/types.go b/cmd/types.go index bdb0fa9..b004e79 100644 --- a/cmd/types.go +++ b/cmd/types.go @@ -193,13 +193,12 @@ func TypesCommand() { flag.Usage = func() { helpMessage := helpBorder.Render( + "Get details about a specific typing.\n\n", styleBold.Render("USAGE:"), fmt.Sprintf("\n\t%s %s %s", "poke-cli", styleBold.Render("types"), "[flag]"), - fmt.Sprintf("\n\t%-30s", "Get details about a specific typing"), - fmt.Sprintf("\n\t%-30s", "----------"), - fmt.Sprintf("\n\t%-30s", styleItalic.Render("Examples:")), - fmt.Sprintf("\n\t%-30s", "poke-cli types"), - fmt.Sprintf("\n\t%-30s", "A table will then display with the option to select a type."), + "\n\n", + styleBold.Render("FLAGS:"), + fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints out the help menu."), ) fmt.Println(helpMessage) } diff --git a/cmd/validateargs.go b/cmd/validateargs.go index b857f21..16c4d91 100644 --- a/cmd/validateargs.go +++ b/cmd/validateargs.go @@ -6,16 +6,33 @@ import ( "os" ) +func handleHelpFlag(args []string) { + if len(args) == 3 && (args[2] == "-h" || args[2] == "--help") { + flag.Usage() + + if flag.Lookup("test.v") == nil { + os.Exit(0) + } + } +} + // ValidatePokemonArgs validates the command line arguments func ValidatePokemonArgs(args []string) error { - if len(args) > 5 { - errMessage := errorBorder.Render(errorColor.Render("Error!"), "\nToo many arguments") + handleHelpFlag(args) + + if len(args) < 3 { + errMessage := errorBorder.Render( + errorColor.Render("Error!"), + "\nPlease declare a Pokémon's name after the [pokemon] command", + "\nRun 'poke-cli pokemon -h' for more details", + "\nerror: insufficient arguments", + ) return fmt.Errorf("%s", errMessage) } - if len(args) < 3 { - errMessage := errorBorder.Render(errorColor.Render("Error!"), "\nPlease declare a Pokémon's name after the [pokemon] command", "\nRun 'poke-cli pokemon -h' for more details", "\nerror: insufficient arguments") + if len(args) > 5 { + errMessage := errorBorder.Render(errorColor.Render("Error!"), "\nToo many arguments") return fmt.Errorf("%s", errMessage) } @@ -31,16 +48,14 @@ func ValidatePokemonArgs(args []string) error { } } - if args[2] == "-h" || args[2] == "--help" { - flag.Usage() - return fmt.Errorf("") - } - return nil } // ValidateTypesArgs validates the command line arguments func ValidateTypesArgs(args []string) error { + + handleHelpFlag(args) + if len(args) > 3 { errMessage := errorBorder.Render(errorColor.Render("Error!"), "\nToo many arguments") return fmt.Errorf("%s", errMessage) @@ -54,16 +69,7 @@ func ValidateTypesArgs(args []string) error { finalErrorMessage := errorTitle + errorString renderedError := errorBorder.Render(finalErrorMessage) return fmt.Errorf("%s", renderedError) - - // Check if there are exactly 3 arguments and the third argument is either '-h' or '--help' - // If true, display the usage information - } else if len(args) == 3 && (args[2] == "-h" || args[2] == "--help") { - flag.Usage() - - // Only call os.Exit if not in test mode - if flag.Lookup("test.v") == nil { - os.Exit(0) - } } + return nil } diff --git a/connections/connection.go b/connections/connection.go index 7fa3308..d61126e 100644 --- a/connections/connection.go +++ b/connections/connection.go @@ -2,10 +2,12 @@ package connections import ( "encoding/json" + "flag" "fmt" "github.com/charmbracelet/lipgloss" "io" "net/http" + "os" ) type PokemonJSONStruct struct { @@ -79,8 +81,13 @@ func ApiCallSetup(url string, target interface{}) error { defer res.Body.Close() if res.StatusCode == http.StatusNotFound { - fmt.Println(errorColor.Render("Page not found. 404 error.")) - return fmt.Errorf("page not found: 404 error") + fmt.Println(errorColor.Render("Pokémon not found. Perhaps a typo in the name?")) + + if flag.Lookup("test.v") != nil { + return fmt.Errorf("page not found: 404 error") + } else { + os.Exit(1) + } } body, err := io.ReadAll(res.Body) diff --git a/flags/version.go b/flags/version.go index 19d0d00..5f4ebe0 100644 --- a/flags/version.go +++ b/flags/version.go @@ -21,14 +21,12 @@ func latestDockerImage() { fmt.Print("Latest Docker image version: ", string(output)) } -func latestRelease() { +func latestRelease(githubAPIURL string) { type Release struct { TagName string `json:"tag_name"` } - url := "https://api.github.com/repos/digitalghost-dev/poke-cli/releases/latest" - - response, err := http.Get(url) + response, err := http.Get(githubAPIURL) if err != nil { fmt.Println("Error fetching data:", err) return @@ -58,5 +56,5 @@ func latestRelease() { func LatestFlag() { // cmd := exec.Command("git", "describe", "--tags", "--abbrev=0") latestDockerImage() - latestRelease() + latestRelease("https://api.github.com/repos/digitalghost-dev/poke-cli/releases/latest") } diff --git a/flags/version_test.go b/flags/version_test.go new file mode 100644 index 0000000..fa06209 --- /dev/null +++ b/flags/version_test.go @@ -0,0 +1,81 @@ +package flags + +import ( + "bytes" + "fmt" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "os" + "testing" +) + +// captureOutput redirects stdout to capture any printed output during a function's execution. +func captureOutput(f func()) string { + // Save the original stdout + oldStdout := os.Stdout + // Create a pipe to capture the output + r, w, _ := os.Pipe() + os.Stdout = w + + // Run the function, capturing its output + f() + + // Restore the original stdout and close the writer + w.Close() + os.Stdout = oldStdout + + // Read the captured output + var buf bytes.Buffer + _, _ = buf.ReadFrom(r) + return buf.String() +} + +func TestLatestDockerImage(t *testing.T) { + output := captureOutput(latestDockerImage) + + // Modify this assertion as needed based on expected output + assert.Contains(t, output, "Latest Docker image version:") +} + +func TestLatestRelease(t *testing.T) { + githubAPIURL := "https://api.github.com/repos/digitalghost-dev/poke-cli/releases/latest" + output := captureOutput(func() { latestRelease(githubAPIURL) }) + + assert.Contains(t, output, "Latest release tag: v") +} + +func TestLatestRelease_Success(t *testing.T) { + // Create a mock server that simulates a successful response + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, `{"tag_name": "v1.0.0"}`) + }) + server := httptest.NewServer(handler) + defer server.Close() + + // Capture output of the function + output := captureOutput(func() { latestRelease(server.URL) }) + assert.Contains(t, output, "Latest release tag: v1.0.0") +} + +func TestLatestRelease_InvalidJSON(t *testing.T) { + // Create a mock server that returns invalid JSON + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, `invalid-json`) + }) + server := httptest.NewServer(handler) + defer server.Close() + + // Capture output of the function + output := captureOutput(func() { latestRelease(server.URL) }) + assert.Contains(t, output, "Error unmarshalling JSON:") +} + +func TestLatestFlag(t *testing.T) { + // Capture the output of the LatestFlag function + output := captureOutput(LatestFlag) + + // Verify that the output contains expected messages from both latestDockerImage and latestRelease + assert.Contains(t, output, "Latest Docker image version:", "Expected output to contain 'Latest Docker image version:' but got: %v", output) + assert.Contains(t, output, "Latest release tag:", "Expected output to contain 'Latest release tag:' but got: %v", output) +} diff --git a/go.mod b/go.mod index 66a5cd4..def091d 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,15 @@ go 1.23.2 require ( github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/bubbletea v1.1.1 - github.com/charmbracelet/lipgloss v0.13.0 + github.com/charmbracelet/bubbletea v1.2.0 + github.com/charmbracelet/lipgloss v1.0.0 github.com/stretchr/testify v1.9.0 - golang.org/x/text v0.15.0 + golang.org/x/text v0.19.0 ) require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/x/ansi v0.2.3 // indirect + github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/term v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect @@ -26,6 +26,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9379f43..e247e8f 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,16 @@ github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQW github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/bubbletea v1.2.0 h1:WYHclJaFDOz4dPxiGx7owwb8P4000lYPcuXPIALS5Z8= +github.com/charmbracelet/bubbletea v1.2.0/go.mod h1:viLoDL7hG4njLJSKU2gw7kB3LSEmWsrM80rO1dBJWBI= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= +github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= @@ -45,8 +51,12 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=