Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.6.5 #59

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
branches:
- main
env:
VERSION_NUMBER: 'v0.6.4'
VERSION_NUMBER: 'v0.6.5'
REGISTRY_NAME: digitalghostdev/poke-cli

jobs:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img height="250" width="350" src="https://cdn.simpleicons.org/pokemon/FFCC00" alt="pokemon-logo"/>
<h1>Pokémon CLI</h1>
<img src="https://img.shields.io/github/v/release/digitalghost-dev/poke-cli?style=flat-square&logo=git&logoColor=FFCC00&label=Release%20Version&labelColor=EEE&color=FFCC00" alt="version-label">
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v0.6.4?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v0.6.5?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
<img src="https://img.shields.io/github/actions/workflow/status/digitalghost-dev/poke-cli/ci.yml?branch=main&style=flat-square&logo=github&logoColor=FFCC00&label=CI&labelColor=EEE&color=FFCC00">
</div>

Expand Down Expand Up @@ -40,7 +40,7 @@ _Taskfile can build the executable for you_
_Use a Docker Image_

```bash
docker run --rm -it digitalghostdev/poke-cli:v0.6.3 [command] [subcommand] [flag]
docker run --rm -it digitalghostdev/poke-cli:v0.6.5 [command] [subcommand] [flag]
```

### Go Build
Expand Down
34 changes: 28 additions & 6 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ var (
BorderForeground(lipgloss.Color("#F2055C"))
)

func main() {
latestFlag := flag.Bool("latest", false, "Prints the program's latest Docker Image and Release versions.")
shortLatestFlag := flag.Bool("l", false, "Prints the program's latest Docker Image and Release versions.")
func runCLI(args []string) int {
mainFlagSet := flag.NewFlagSet("poke-cli", flag.ContinueOnError)
latestFlag := mainFlagSet.Bool("latest", false, "Prints the program's latest Docker Image and Release versions.")
shortLatestFlag := mainFlagSet.Bool("l", false, "Prints the program's latest Docker Image and Release versions.")

flag.Usage = func() {
mainFlagSet.Usage = func() {
helpMessage := helpBorder.Render(
"Welcome! This tool displays data related to Pokémon!",
"\n\n", styleBold.Render("USAGE:"),
Expand All @@ -42,19 +43,33 @@ func main() {
fmt.Println(helpMessage)
}

flag.Parse()
// Check for help flag manually
for _, arg := range args {
if arg == "-h" || arg == "--help" {
mainFlagSet.Usage()
return 0
}
}

err := mainFlagSet.Parse(args)
if err != nil {
return 2
}

commands := map[string]func(){
"pokemon": cmd.PokemonCommand,
"types": cmd.TypesCommand,
}

if len(os.Args) < 2 {
flag.Usage()
mainFlagSet.Usage()
return 1
} else if *latestFlag || *shortLatestFlag {
flags.LatestFlag()
return 0
} else if cmdFunc, exists := commands[os.Args[1]]; exists {
cmdFunc()
return 0
} else {
errMessage := errorBorder.Render(
errorColor.Render("Error!"),
Expand All @@ -64,5 +79,12 @@ func main() {
fmt.Sprintf("\nAlso run %s for more info!", styleBold.Render("[poke-cli -h]")),
)
fmt.Printf("%s\n", errMessage)
return 1
}
}

var exit = os.Exit // Default to os.Exit, but you can override this in tests

func main() {
exit(runCLI(os.Args[1:]))
}
174 changes: 59 additions & 115 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,37 @@ package main

import (
"bytes"
"os/exec"
"fmt"
"io"
"os"
"regexp"
"testing"
)

func TestCLI(t *testing.T) {
// Strip ANSI color codes
var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)

func stripANSI(input string) string {
return ansiRegex.ReplaceAllString(input, "")
}

func TestMainFunction(t *testing.T) {
version := "v0.6.4"

// Backup the original exit function and stdout/stderr
originalExit := exit
originalStdout := os.Stdout
originalStderr := os.Stderr
defer func() {
exit = originalExit // Restore exit
os.Stdout = originalStdout // Restore stdout
os.Stderr = originalStderr // Restore stderr
}()

// Replace exit with a function that captures the exit code
exitCode := 0
exit = func(code int) { exitCode = code }

tests := []struct {
args []string
expectedOutput string
Expand All @@ -22,94 +48,20 @@ func TestCLI(t *testing.T) {
"│ │\n" +
"│Also run [poke-cli -h] for more info! │\n" +
"╰──────────────────────────────────────────────────────╯\n",
expectedExit: 0,
},
{
args: []string{"pokemon"},
expectedOutput: "╭────────────────────────────────────────────────────────────╮\n" +
"│Error! │\n" +
"│Please declare a Pokémon's name after the [pokemon] command │\n" +
"│Run 'poke-cli pokemon -h' for more details │\n" +
"│error: insufficient arguments │\n" +
"╰────────────────────────────────────────────────────────────╯\n",
expectedExit: 1,
},
{
args: []string{"pokemon", "bulbasaur"},
expectedOutput: "Your selected Pokémon: Bulbasaur\nNational Pokédex #: 1\n",
args: []string{"-l"},
expectedOutput: fmt.Sprintf("Latest Docker image version: %s\nLatest release tag: %s\n", version, version),
expectedExit: 0,
},
{
args: []string{"pokemon", "mew", "--types"},
expectedOutput: "Your selected Pokémon: Mew\nNational Pokédex #: 151\n──────\nTyping\nType 1: psychic\n",
args: []string{"--latest"},
expectedOutput: fmt.Sprintf("Latest Docker image version: %s\nLatest release tag: %s\n", version, version),
expectedExit: 0,
},
{
args: []string{"pokemon", "cacturne", "--types"},
expectedOutput: "Your selected Pokémon: Cacturne\nNational Pokédex #: 332\n──────\nTyping\nType 1: grass\nType 2: dark\n",
expectedExit: 0,
},
{
args: []string{"pokemon", "chimchar", "types"},
expectedOutput: "╭─────────────────────────────────────────────────────────────────────────────────╮\n" +
"│Error! │\n" +
"│Invalid argument 'types'. Only flags are allowed after declaring a Pokémon's name│\n" +
"╰─────────────────────────────────────────────────────────────────────────────────╯\n",
expectedExit: 1,
},
{
args: []string{"pokemon", "flutter-mane", "types"},
expectedOutput: "╭─────────────────────────────────────────────────────────────────────────────────╮\n" +
"│Error! │\n" +
"│Invalid argument 'types'. Only flags are allowed after declaring a Pokémon's name│\n" +
"╰─────────────────────────────────────────────────────────────────────────────────╯\n",
expectedExit: 1,
},
{
args: []string{
"pokemon", "AmPhaROs", "--types", "--abilities",
},
expectedOutput: "Your selected Pokémon: Ampharos\n" +
"National Pokédex #: 181\n" +
"──────\n" +
"Typing\n" +
"Type 1: electric\n" +
"─────────\n" +
"Abilities\n" +
"Ability 1: static\n" +
"Hidden Ability: plus\n",
expectedExit: 0,
},
{
args: []string{
"pokemon", "CLOysTeR", "-t", "-a",
},
expectedOutput: "Your selected Pokémon: Cloyster\n" +
"National Pokédex #: 91\n" +
"──────\n" +
"Typing\n" +
"Type 1: water\n" +
"Type 2: ice\n" +
"─────────\n" +
"Abilities\n" +
"Ability 1: shell-armor\n" +
"Ability 2: skill-link\n" +
"Hidden Ability: overcoat\n",
expectedExit: 0,
},
{
args: []string{"pokemon", "gyarados", "--help"},
expectedOutput: "╭──────────────────────────────────────────────────────────────╮\n" +
"│poke-cli pokemon <pokemon-name> [flags] │\n" +
"│ │\n" +
"│FLAGS: │\n" +
"│ -a, --abilities Prints out the Pokémon's abilities. │\n" +
"│ -t, --types Prints out the Pokémon's typing. │\n" +
"╰──────────────────────────────────────────────────────────────╯\n",
expectedExit: 0,
},
{
args: []string{"--help"},
args: []string{"-h"},
expectedOutput: "╭──────────────────────────────────────────────────────╮\n" +
"│Welcome! This tool displays data related to Pokémon! │\n" +
"│ │\n" +
Expand All @@ -129,50 +81,42 @@ func TestCLI(t *testing.T) {
"╰──────────────────────────────────────────────────────╯\n",
expectedExit: 0,
},
{
args: []string{"types", "ground", "all"},
expectedOutput: "╭──────────────────╮\n" +
"│Error! │\n" +
"│Too many arguments│\n" +
"╰──────────────────╯\n",
expectedExit: 1,
},
{
args: []string{"types", "--help"},
expectedOutput: "╭───────────────────────────────────────────────────────────────╮\n" +
"│USAGE: │\n" +
"│ poke-cli types [flag] │\n" +
"│ Get details about a specific typing │\n" +
"│ ---------- │\n" +
"│ Examples: │\n" +
"│ poke-cli types │\n" +
"│ A table will then display with the option to select a type.│\n" +
"╰───────────────────────────────────────────────────────────────╯\n",
expectedExit: 0,
},
}

for _, test := range tests {
cmd := exec.Command("poke-cli", test.args...)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
// Create a pipe to capture output
r, w, _ := os.Pipe()
os.Stdout = w
os.Stderr = w

// Set os.Args for the test case
os.Args = append([]string{"poke-cli"}, test.args...)

err := cmd.Run()
// Run the main function
main()

// Close the writer and restore stdout and stderr
err := w.Close()
if err != nil {
// If there's an error, but we expected a successful exit
if test.expectedExit == 0 {
t.Errorf("Unexpected error: %v", err)
}
t.Fatalf("Error closing pipe writer: %v", err)
}
os.Stdout = originalStdout
os.Stderr = originalStderr

// Read from the pipe
var buf bytes.Buffer
if _, err := io.Copy(&buf, r); err != nil {
t.Errorf("Error copying output: %v", err)
}

if out.String() != test.expectedOutput {
t.Errorf("Args: %v, Expected output: %q, Got: %q", test.args, test.expectedOutput, out.String())
// Strip ANSI color codes from the actual output
actualOutput := stripANSI(buf.String())
if actualOutput != test.expectedOutput {
t.Errorf("Args: %v\nExpected output: %q\nGot: %q\n", test.args, test.expectedOutput, actualOutput)
}

if cmd.ProcessState.ExitCode() != test.expectedExit {
t.Errorf("Args: %v, Expected exit code: %d, Got: %d", test.args, test.expectedExit, cmd.ProcessState.ExitCode())
if exitCode != test.expectedExit {
t.Errorf("Args: %v\nExpected exit code: %d\nGot: %d\n", test.args, test.expectedExit, exitCode)
}
}
}
12 changes: 6 additions & 6 deletions flags/pokemonflagset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ func TestSetupPokemonFlagSet(t *testing.T) {

// Check types flag
assert.NotNil(t, typesFlag, "Types flag should not be nil")
assert.Equal(t, bool(false), *typesFlag, "Types flag name should be 'types'")
assert.Equal(t, false, *typesFlag, "Types flag name should be 'types'")

// Check short types flag
assert.NotNil(t, shortTypesFlag, "Types flag should not be nil")
assert.Equal(t, bool(false), *shortTypesFlag, "Types flag name should be 't'")
assert.NotNil(t, shortTypesFlag, "Short types flag should not be nil")
assert.Equal(t, false, *shortTypesFlag, "Short types flag name should be 't'")

// Check abilities flag
assert.NotNil(t, abilitiesFlag, "Abilities flag should not be nil")
assert.Equal(t, bool(false), *abilitiesFlag, "Abilities flag name should be 'abilities'")
assert.Equal(t, false, *abilitiesFlag, "Abilities flag name should be 'abilities'")

// Check short abilities flag
assert.NotNil(t, shortAbilitiesFlag, "Abilities flag should not be nil")
assert.Equal(t, bool(false), *shortAbilitiesFlag, "Abilities flag name should be 'a'")
assert.NotNil(t, shortAbilitiesFlag, "Short abilities flag should not be nil")
assert.Equal(t, false, *shortAbilitiesFlag, "Short abilities flag name should be 'a'")
}