diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a7ab943..10ac012 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,6 +15,8 @@ on:
paths-ignore:
- 'README.md'
- '.github/**'
+ - '.dockerignore'
+ - '.gitignore'
- 'demo**'
- 'go.mod'
- 'go.sum'
@@ -22,11 +24,12 @@ on:
branches:
- main
env:
- VERSION_NUMBER: 'v0.8.0'
- REGISTRY_NAME: digitalghostdev/poke-cli
+ VERSION_NUMBER: 'v0.9.0'
+ DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli'
+ AWS_REGION: 'us-west-2'
jobs:
- snyk:
+ gosec:
runs-on: ubuntu-22.04
permissions:
@@ -38,23 +41,20 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - name: Run Snyk
- uses: snyk/actions/golang@master
- continue-on-error: true
- env:
- SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
+ - name: Run Gosec Security Scanner
+ uses: securego/gosec@master
with:
- args: --sarif-file-output=snyk.sarif --skip-unresolved=true
+ args: '-no-fail -fmt sarif -out results.sarif ./...'
- - name: Upload Result to GitHub Code Scanning
- uses: github/codeql-action/upload-sarif@v2
+ - name: Upload SARIF Report
+ uses: github/codeql-action/upload-sarif@v3
with:
- sarif_file: snyk.sarif
+ sarif_file: results.sarif
build-docker-image:
runs-on: ubuntu-22.04
- needs: [snyk]
- if: needs.snyk.result == 'success'
+ needs: [gosec]
+ if: needs.gosec.result == 'success'
steps:
- name: Checkout
@@ -81,6 +81,33 @@ jobs:
name: poke-cli
path: /tmp/poke-cli.tar
+ # Uploading to Elastic Container Registry has a backup method.
+ upload-to-ecr:
+ runs-on: ubuntu-22.04
+ needs: [build-docker-image]
+ if: needs.build-docker-image.result == 'success'
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Configure AWS
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ env.AWS_REGION }}
+
+ - name: Login to Amazon ECR
+ id: login-ecr
+ uses: aws-actions/amazon-ecr-login@v2
+
+ - name: Build, tag, and push image to Amazon ECR
+ run : |
+ docker build -t poke-cli:${{ env.VERSION_NUMBER }} .
+ docker tag poke-cli:${{ env.VERSION_NUMBER }} ${{ secrets.AWS_ECR_NAME }}:${{ env.VERSION_NUMBER }}
+ docker push ${{ secrets.AWS_ECR_NAME }}:${{ env.VERSION_NUMBER }}
+
syft:
permissions:
contents: 'read'
@@ -150,8 +177,8 @@ jobs:
architecture-build:
runs-on: ubuntu-22.04
- needs: [snyk]
- if: needs.snyk.result == 'success'
+ needs: [gosec]
+ if: needs.gosec.result == 'success'
strategy:
fail-fast: false
@@ -166,7 +193,7 @@ jobs:
id: meta
uses: 'docker/metadata-action@v5.0.0'
with:
- images: ${{ env.REGISTRY_NAME }}
+ images: ${{ env.DOCKERHUB_REGISTRY_NAME }}
- name: Set up QEMU
uses: 'docker/setup-qemu-action@v3'
@@ -187,7 +214,7 @@ jobs:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
- outputs: type=image,name=${{ env.REGISTRY_NAME }},push-by-digest=true,name-canonical=true,push=true
+ outputs: type=image,name=${{ env.DOCKERHUB_REGISTRY_NAME }},push-by-digest=true,name-canonical=true,push=true
- name: Export Digest
run: |
@@ -232,7 +259,7 @@ jobs:
id: meta
uses: 'docker/metadata-action@v5.0.0'
with:
- images: ${{ env.REGISTRY_NAME }}
+ images: ${{ env.DOCKERHUB_REGISTRY_NAME }}
tags: ${{ env.VERSION_NUMBER }}
- name: Login to Docker Hub
@@ -245,8 +272,8 @@ jobs:
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
- $(printf '${{ env.REGISTRY_NAME }}@sha256:%s ' *)
+ $(printf '${{ env.DOCKERHUB_REGISTRY_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
- docker buildx imagetools inspect ${{ env.REGISTRY_NAME }}:${{ steps.meta.outputs.version }}
+ docker buildx imagetools inspect ${{ env.DOCKERHUB_REGISTRY_NAME }}:${{ steps.meta.outputs.version }}
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index c8be533..46b9119 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -13,6 +13,8 @@ builds:
- linux
- windows
- darwin
+ ldflags:
+ - -s -w -X main.version=v0.9.0
archives:
- format: tar.gz
diff --git a/Dockerfile b/Dockerfile
index 342cc05..8f6408f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,14 +1,21 @@
-FROM golang:1.23-alpine3.19
+# build 1
+FROM golang:1.23-alpine3.19 AS build
WORKDIR /app
-ENV TERM=xterm-256color
-ENV COLOR_OUTPUT=true
+COPY go.mod go.sum ./
+RUN go mod download
+
+COPY . .
-COPY . /app
+RUN go build -ldflags "-X main.version=v0.9.0" -o poke-cli .
-RUN PATH="$PATH:~/go/bin:/usr/local/go/bin:$GOPATH/bin"
+# build 2
+FROM gcr.io/distroless/static-debian12:nonroot
-RUN go install
+COPY --from=build /app/poke-cli /app/poke-cli
+
+ENV TERM=xterm-256color
+ENV COLOR_OUTPUT=true
-ENTRYPOINT ["poke-cli"]
\ No newline at end of file
+ENTRYPOINT ["/app/poke-cli"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 2cd321b..5ec9bb6 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Pokémon CLI
-
+
@@ -19,7 +19,7 @@ My aim is to have four commands finished for `v1.0.0`. Read more in the [Roadmap
---
## Demo
-
+
---
## Install
@@ -68,18 +68,17 @@ _Use a Docker Image_
* Necessary.
```bash
-docker run --rm -i -t digitalghostdev/poke-cli:v0.8.0 [subcommand] flag]
+docker run --rm -i -t digitalghostdev/poke-cli:v0.9.0 [subcommand] flag]
```
### Go Install
-_Install the executable yourself_
+_If you have Go already, install the executable yourself_
-1. Install [Golang](https://go.dev/dl/).
-2. Once installed, run the following command:
+1. Run the following command:
```bash
go install github.com/digitalghost-dev/poke-cli@v0
```
-3. The tool is ready to use!
+2. The tool is ready to use!
---
## Usage
By running `poke-cli [-h | --help]`, it'll display information on how to use the tool.
diff --git a/cli.go b/cli.go
index 488447b..bfccc98 100644
--- a/cli.go
+++ b/cli.go
@@ -7,6 +7,7 @@ import (
"github.com/digitalghost-dev/poke-cli/cmd"
"github.com/digitalghost-dev/poke-cli/flags"
"os"
+ "runtime/debug"
)
var (
@@ -20,10 +21,39 @@ var (
BorderForeground(lipgloss.Color("#F2055C"))
)
+var version = "(devel)"
+
+func currentVersion() {
+ if version != "(devel)" {
+ // Use version injected by -ldflags
+ fmt.Printf("Version: %s\n", version)
+ return
+ }
+
+ // Fallback to build info when version is not set
+ buildInfo, ok := debug.ReadBuildInfo()
+ if !ok {
+ fmt.Println("Version: unknown (unable to read build info)")
+ return
+ }
+
+ if buildInfo.Main.Version != "" {
+ fmt.Printf("Version: %s\n", buildInfo.Main.Version)
+ } else {
+ fmt.Println("Version: (devel)")
+ }
+}
+
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.")
+
+ // -l, --latest flag retrieves the latest Docker image and GitHub release versions available
+ 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.")
+
+ // -v, --version flag retrives the currently installed version
+ currentVersionFlag := mainFlagSet.Bool("version", false, "Prints the current version")
+ shortCurrentVersionFlag := mainFlagSet.Bool("v", false, "Prints the current version")
mainFlagSet.Usage = func() {
helpMessage := helpBorder.Render(
@@ -34,8 +64,8 @@ func runCLI(args []string) int {
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"),
- fmt.Sprintf("\n\t%-15s %s", "", "version of the program"),
+ fmt.Sprintf("\n\t%-15s %s", "-l, --latest", "Prints the latest version available"),
+ fmt.Sprintf("\n\t%-15s %s", "-v, --version", "Prints the current version"),
"\n\n", styleBold.Render("AVAILABLE 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"),
@@ -70,6 +100,9 @@ func runCLI(args []string) int {
} else if *latestFlag || *shortLatestFlag {
flags.LatestFlag()
return 0
+ } else if *currentVersionFlag || *shortCurrentVersionFlag {
+ currentVersion()
+ return 0
} else if cmdFunc, exists := commands[os.Args[1]]; exists {
cmdFunc()
return 0
diff --git a/cli_test.go b/cli_test.go
index ad5f0d5..c0876d9 100644
--- a/cli_test.go
+++ b/cli_test.go
@@ -38,67 +38,67 @@ func TestRunCLI(t *testing.T) {
{
name: "No Arguments",
args: []string{},
- expectedOutput: "╭──────────────────────────────────────────────────────╮\n" +
- "│Welcome! This tool displays data related to Pokémon! │\n" +
- "│ │\n" +
- "│ USAGE: │\n" +
- "│ poke-cli [flag] │\n" +
- "│ poke-cli [flag] │\n" +
- "│ poke-cli [flag] │\n" +
- "│ │\n" +
- "│ FLAGS: │\n" +
- "│ -h, --help Shows the help menu │\n" +
- "│ -l, --latest Prints the latest available │\n" +
- "│ version of the program │\n" +
- "│ │\n" +
- "│ AVAILABLE COMMANDS: │\n" +
- "│ pokemon Get details of a specific Pokémon │\n" +
- "│ types Get details of a specific typing │\n" +
- "╰──────────────────────────────────────────────────────╯\n",
+ expectedOutput: "╭────────────────────────────────────────────────────────╮\n" +
+ "│Welcome! This tool displays data related to Pokémon! │\n" +
+ "│ │\n" +
+ "│ USAGE: │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ │\n" +
+ "│ FLAGS: │\n" +
+ "│ -h, --help Shows the help menu │\n" +
+ "│ -l, --latest Prints the latest version available │\n" +
+ "│ -v, --version Prints the current version │\n" +
+ "│ │\n" +
+ "│ AVAILABLE COMMANDS: │\n" +
+ "│ pokemon Get details of a specific Pokémon │\n" +
+ "│ types Get details of a specific typing │\n" +
+ "╰────────────────────────────────────────────────────────╯\n",
expectedCode: 0,
},
{
name: "Help Flag Short",
args: []string{"-h"},
- expectedOutput: "╭──────────────────────────────────────────────────────╮\n" +
- "│Welcome! This tool displays data related to Pokémon! │\n" +
- "│ │\n" +
- "│ USAGE: │\n" +
- "│ poke-cli [flag] │\n" +
- "│ poke-cli [flag] │\n" +
- "│ poke-cli [flag] │\n" +
- "│ │\n" +
- "│ FLAGS: │\n" +
- "│ -h, --help Shows the help menu │\n" +
- "│ -l, --latest Prints the latest available │\n" +
- "│ version of the program │\n" +
- "│ │\n" +
- "│ AVAILABLE COMMANDS: │\n" +
- "│ pokemon Get details of a specific Pokémon │\n" +
- "│ types Get details of a specific typing │\n" +
- "╰──────────────────────────────────────────────────────╯\n",
+ expectedOutput: "╭────────────────────────────────────────────────────────╮\n" +
+ "│Welcome! This tool displays data related to Pokémon! │\n" +
+ "│ │\n" +
+ "│ USAGE: │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ │\n" +
+ "│ FLAGS: │\n" +
+ "│ -h, --help Shows the help menu │\n" +
+ "│ -l, --latest Prints the latest version available │\n" +
+ "│ -v, --version Prints the current version │\n" +
+ "│ │\n" +
+ "│ AVAILABLE COMMANDS: │\n" +
+ "│ pokemon Get details of a specific Pokémon │\n" +
+ "│ types Get details of a specific typing │\n" +
+ "╰────────────────────────────────────────────────────────╯\n",
expectedCode: 0,
},
{
name: "Help Flag Long",
args: []string{"--help"},
- expectedOutput: "╭──────────────────────────────────────────────────────╮\n" +
- "│Welcome! This tool displays data related to Pokémon! │\n" +
- "│ │\n" +
- "│ USAGE: │\n" +
- "│ poke-cli [flag] │\n" +
- "│ poke-cli [flag] │\n" +
- "│ poke-cli [flag] │\n" +
- "│ │\n" +
- "│ FLAGS: │\n" +
- "│ -h, --help Shows the help menu │\n" +
- "│ -l, --latest Prints the latest available │\n" +
- "│ version of the program │\n" +
- "│ │\n" +
- "│ AVAILABLE COMMANDS: │\n" +
- "│ pokemon Get details of a specific Pokémon │\n" +
- "│ types Get details of a specific typing │\n" +
- "╰──────────────────────────────────────────────────────╯\n",
+ expectedOutput: "╭────────────────────────────────────────────────────────╮\n" +
+ "│Welcome! This tool displays data related to Pokémon! │\n" +
+ "│ │\n" +
+ "│ USAGE: │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ poke-cli [flag] │\n" +
+ "│ │\n" +
+ "│ FLAGS: │\n" +
+ "│ -h, --help Shows the help menu │\n" +
+ "│ -l, --latest Prints the latest version available │\n" +
+ "│ -v, --version Prints the current version │\n" +
+ "│ │\n" +
+ "│ AVAILABLE COMMANDS: │\n" +
+ "│ pokemon Get details of a specific Pokémon │\n" +
+ "│ types Get details of a specific typing │\n" +
+ "╰────────────────────────────────────────────────────────╯\n",
expectedCode: 0,
},
{
@@ -110,7 +110,7 @@ func TestRunCLI(t *testing.T) {
{
name: "Latest Flag",
args: []string{"-l"},
- expectedOutput: "Latest Docker image version: v0.7.2\nLatest release tag: v0.7.2\n",
+ expectedOutput: "Latest Docker image version: v0.8.0\nLatest release tag: v0.8.0\n",
expectedCode: 0,
},
}
diff --git a/cmd/types.go b/cmd/types.go
index dd28069..4e5bf5d 100644
--- a/cmd/types.go
+++ b/cmd/types.go
@@ -69,7 +69,7 @@ func displayTypeDetails(typesName string, endpoint string) {
selectedType := cases.Title(language.English).String(typeName)
coloredType := lipgloss.NewStyle().Foreground(lipgloss.Color(getTypeColor(typeName))).Render(selectedType)
- fmt.Printf("You selected the %s type.\nNumber of Pokémon with type: %d\n", coloredType, len(typesStruct.Pokemon))
+ fmt.Printf("You selected the %s type.\nNumber of Pokémon with type: %d\nNumber of moves with type: %d\n", coloredType, len(typesStruct.Pokemon), len(typesStruct.Moves))
fmt.Println("----------")
fmt.Println(styleBold.Render("Damage Chart:"))
diff --git a/cmd/validateargs_test.go b/cmd/validateargs_test.go
index 624915b..14dc76b 100644
--- a/cmd/validateargs_test.go
+++ b/cmd/validateargs_test.go
@@ -1,13 +1,18 @@
package cmd
import (
+ "regexp"
+ "strings"
"testing"
)
+func stripANSI(input string) string {
+ ansiEscape := regexp.MustCompile(`\x1b\[[0-9;]*m`)
+ return ansiEscape.ReplaceAllString(input, "")
+}
+
// TestValidatePokemonArgs tests the ValidatePokemonArgs function
func TestValidatePokemonArgs(t *testing.T) {
-
- // Test case: Too few arguments
args := []string{"poke-cli", "pokemon"}
expectedError := "╭────────────────────────────────────────────────────────────╮\n" +
"│Error! │\n" +
@@ -16,8 +21,17 @@ func TestValidatePokemonArgs(t *testing.T) {
"│error: insufficient arguments │\n" +
"╰────────────────────────────────────────────────────────────╯"
err := ValidatePokemonArgs(args)
- if err == nil || err.Error() != expectedError {
- t.Errorf("Expected error for too few arguments, got: %v", err)
+ if err == nil {
+ t.Errorf("Expected error for too few arguments, got nil")
+ return
+ }
+
+ // Strip ANSI codes for comparison
+ actualError := stripANSI(err.Error())
+ expectedError = strings.TrimSpace(expectedError)
+
+ if actualError != expectedError {
+ t.Errorf("Expected error:\n%s\nGot:\n%s", expectedError, actualError)
}
}
diff --git a/connections/connection.go b/connections/connection.go
index df2c12f..071afeb 100644
--- a/connections/connection.go
+++ b/connections/connection.go
@@ -2,11 +2,13 @@ package connections
import (
"encoding/json"
+ "errors"
"flag"
"fmt"
"github.com/charmbracelet/lipgloss"
"io"
"net/http"
+ "net/url"
"os"
)
@@ -39,8 +41,12 @@ type PokemonJSONStruct struct {
}
type TypesJSONStruct struct {
- Name string `json:"name"`
- ID int `json:"id"`
+ Name string `json:"name"`
+ ID int `json:"id"`
+ Moves []struct {
+ Name string `json:"name"`
+ URL string `json:"url"`
+ } `json:"moves"`
Pokemon []struct {
Pokemon struct {
Name string `json:"name"`
@@ -80,8 +86,22 @@ var red = lipgloss.Color("#F2055C")
var errorColor = lipgloss.NewStyle().Foreground(red)
// ApiCallSetup Helper function to handle API calls and JSON unmarshalling
-func ApiCallSetup(url string, target interface{}) error {
- res, err := http.Get(url)
+func ApiCallSetup(rawURL string, target interface{}, skipHTTPSCheck bool) error {
+ parsedURL, err := url.Parse(rawURL)
+ if err != nil {
+ return fmt.Errorf("invalid URL provided: %w", err)
+ }
+
+ // Check if running in a test environment
+ if flag.Lookup("test.v") != nil {
+ skipHTTPSCheck = true
+ }
+
+ if !skipHTTPSCheck && parsedURL.Scheme != "https" {
+ return errors.New("only HTTPS URLs are allowed for security reasons")
+ }
+
+ res, err := http.Get(parsedURL.String())
if err != nil {
return fmt.Errorf("error making GET request: %w", err)
}
@@ -111,12 +131,12 @@ func ApiCallSetup(url string, target interface{}) error {
}
func PokemonApiCall(endpoint string, pokemonName string, baseURL string) (PokemonJSONStruct, string, int, int, int) {
+ fullURL := baseURL + endpoint + "/" + pokemonName
- url := baseURL + endpoint + "/" + pokemonName
var pokemonStruct PokemonJSONStruct
-
- err := ApiCallSetup(url, &pokemonStruct)
+ err := ApiCallSetup(fullURL, &pokemonStruct, false)
if err != nil {
+ fmt.Printf("Error in ApiCallSetup: %v\n", err) // Debugging
return PokemonJSONStruct{}, "", 0, 0, 0
}
@@ -125,10 +145,10 @@ func PokemonApiCall(endpoint string, pokemonName string, baseURL string) (Pokemo
func TypesApiCall(endpoint string, typesName string, baseURL string) (TypesJSONStruct, string, int) {
- url := baseURL + endpoint + "/" + typesName
+ fullURL := baseURL + endpoint + "/" + typesName
var typesStruct TypesJSONStruct
- err := ApiCallSetup(url, &typesStruct)
+ err := ApiCallSetup(fullURL, &typesStruct, false)
if err != nil {
return TypesJSONStruct{}, "", 0
}
diff --git a/connections/connection_test.go b/connections/connection_test.go
index 05f2f9d..32a47b9 100644
--- a/connections/connection_test.go
+++ b/connections/connection_test.go
@@ -2,17 +2,17 @@ package connections
import (
"encoding/json"
- "fmt"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
-// TestBaseApiCall - Test for the ApiCallSetup function
-func TestBaseApiCall(t *testing.T) {
+// TestApiCallSetup - Test for the ApiCallSetup function
+func TestApiCallSetup(t *testing.T) {
expectedData := map[string]string{"key": "value"}
+ // Create a test server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(expectedData)
@@ -22,15 +22,13 @@ func TestBaseApiCall(t *testing.T) {
var target map[string]string
- err := ApiCallSetup(ts.URL, &target)
- if err != nil {
- return
- }
+ // Call ApiCallSetup with skipHTTPSCheck set to true
+ err := ApiCallSetup(ts.URL, &target, true)
+ assert.Nil(t, err, "Expected no error for skipHTTPSCheck")
- assert.Equal(t, expectedData, target)
+ assert.Equal(t, expectedData, target, "Expected data does not match the response")
}
-// TestPokemonApiCall - Test for the PokemonApiCall function
func TestPokemonApiCall(t *testing.T) {
expectedPokemon := PokemonJSONStruct{
Name: "pikachu",
@@ -54,17 +52,17 @@ func TestPokemonApiCall(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(expectedPokemon)
- assert.Nil(t, err)
+ assert.Nil(t, err, "failed to encode mock response")
}))
defer ts.Close()
pokemon, name, id, weight, height := PokemonApiCall("/pokemon", "pikachu", ts.URL)
- assert.Equal(t, expectedPokemon, pokemon)
- assert.Equal(t, "pikachu", name)
- assert.Equal(t, 25, id)
- assert.Equal(t, 60, weight)
- assert.Equal(t, 4, height)
+ assert.Equal(t, expectedPokemon, pokemon, "Expected Pokémon struct does not match")
+ assert.Equal(t, "pikachu", name, "Expected name does not match")
+ assert.Equal(t, 25, id, "Expected ID does not match")
+ assert.Equal(t, 60, weight, "Expected weight does not match")
+ assert.Equal(t, 4, height, "Expected height does not match")
}
// TestTypesApiCall - Test for the TypesApiCall function
@@ -100,65 +98,3 @@ func TestTypesApiCall(t *testing.T) {
assert.Equal(t, "electric", name)
assert.Equal(t, 13, id)
}
-
-func TestApiCallSetup_NotFound(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusNotFound)
- fmt.Println(w, `{"error": "not found"}`)
- }))
- defer ts.Close()
-
- var target map[string]string
- err := ApiCallSetup(ts.URL, &target)
- if err != nil {
- return
- }
- // TODO: Add assertions for the output or error message handling
-}
-
-func TestPokemonApiCall_UnmarshalError(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusOK)
- fmt.Println(w, `{"name": "123", "id": "not_a_number"}`) // Partially malformed JSON
- }))
- defer ts.Close()
-
- var pokemonStruct PokemonJSONStruct
- err := ApiCallSetup(ts.URL, &pokemonStruct)
- assert.NotNil(t, err, "Expected unmarshalling error due to type mismatch")
-
- var typesStruct TypesJSONStruct
- err = ApiCallSetup(ts.URL, &typesStruct)
- assert.NotNil(t, err, "Expected unmarshalling error due to type mismatch")
-}
-
-func TestTypesApiCall_SuccessWithAllFields(t *testing.T) {
- expectedTypes := TypesJSONStruct{
- Name: "electric",
- ID: 13,
- // TODO: Add fields to test complex struct parsing like `DamageRelations`
- }
-
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusOK)
- err := json.NewEncoder(w).Encode(expectedTypes)
- assert.Nil(t, err)
- }))
- defer ts.Close()
-
- types, _, _ := TypesApiCall("/type", "electric", ts.URL)
- assert.Equal(t, expectedTypes, types)
-}
-
-func TestApiCallSetup_Handles404(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusNotFound)
- }))
- defer ts.Close()
-
- var target map[string]string
- err := ApiCallSetup(ts.URL, &target)
-
- assert.NotNil(t, err)
- assert.Contains(t, err.Error(), "404 error")
-}
diff --git a/flags/version.go b/flags/version.go
index 4d20342..a653752 100644
--- a/flags/version.go
+++ b/flags/version.go
@@ -2,9 +2,11 @@ package flags
import (
"encoding/json"
+ "flag"
"fmt"
"io"
"net/http"
+ "net/url"
"os/exec"
)
@@ -24,7 +26,27 @@ func latestRelease(githubAPIURL string) {
TagName string `json:"tag_name"`
}
- response, err := http.Get(githubAPIURL)
+ // Parse and validate the URL
+ parsedURL, err := url.Parse(githubAPIURL)
+ if err != nil {
+ fmt.Println("Invalid URL:", err)
+ return
+ }
+
+ // Enforce HTTPS and specific host unless in test mode
+ if flag.Lookup("test.v") == nil { // Check if not in test mode
+ if parsedURL.Scheme != "https" {
+ fmt.Println("Only HTTPS URLs are allowed for security reasons")
+ return
+ }
+ if parsedURL.Host != "api.github.com" {
+ fmt.Println("URL host is not allowed")
+ return
+ }
+ }
+
+ // Make the HTTP GET request
+ response, err := http.Get(parsedURL.String())
if err != nil {
fmt.Println("Error fetching data:", err)
return