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

chore: add IPC unit tests #5777

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
51 changes: 31 additions & 20 deletions cliv2/internal/cliv2/cliv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ const (
ERROR_HAS_BEEN_DISPLAYED = "hasBeenDisplayed"
)

var (
ErrIPCNotNeeded = errors.New("no IPC communication was needed")
ErrIPCNoDataSent = errors.New("no data was sent through the IPC")
ErrIPCFailedToRead = errors.New("error while reading IPC file")
ErrIPCFailedToDeserialize = errors.New("error while deserializing IPC file")
)

func NewCLIv2(config configuration.Configuration, debugLogger *log.Logger, ri runtimeinfo.RuntimeInfo) (*CLI, error) {
cacheDirectory := config.GetString(configuration.CACHE_PATH)

Expand Down Expand Up @@ -461,51 +468,55 @@ func (c *CLI) executeV1Default(proxyInfo *proxy.ProxyInfo, passThroughArgs []str
return ctx.Err()
}

if err == nil {
return nil
}

if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() < 2 {
sentErr, ipcErr := GetErrorFromFile(err, filePath, c.globalConfig)
if ipcErr != nil {
if !errors.Is(ipcErr, ErrIPCNotNeeded) {
c.DebugLogger.Println("IPC: ", ipcErr.Error())
}
return err
}

if sentErrs, fileErr := c.getErrorFromFile(filePath); fileErr == nil {
err = errors.Join(err, sentErrs)
return sentErr
}

func GetErrorFromFile(execErr error, errFilePath string, config configuration.Configuration) (data error, ipcReadErr error) {
if execErr == nil {
return nil, ErrIPCNotNeeded
}

return err
}
if exitErr, ok := execErr.(*exec.ExitError); ok && exitErr.ExitCode() < 2 {
return nil, ErrIPCNotNeeded
}

func (c *CLI) getErrorFromFile(errFilePath string) (data error, err error) {
bytes, fileErr := os.ReadFile(errFilePath)
if os.IsNotExist(fileErr) {
return nil, ErrIPCNoDataSent
}

if fileErr != nil {
c.DebugLogger.Println("Failed to read error file: ", fileErr)
return nil, fileErr
return nil, fmt.Errorf("%w: %w", ErrIPCFailedToRead, fileErr)
}

jsonErrors, serErr := snyk_errors.FromJSONAPIErrorBytes(bytes)
if serErr != nil {
c.DebugLogger.Println("Failed to deserialize file: ", serErr)
return nil, fileErr
return nil, fmt.Errorf("%w: %w", ErrIPCFailedToDeserialize, serErr)
}

if len(jsonErrors) != 0 {
hasBeenDisplayed := GetErrorDisplayStatus(c.globalConfig)
hasBeenDisplayed := GetErrorDisplayStatus(config)

errs := make([]error, len(jsonErrors)+1)
errs = append(errs, execErr)
for _, jerr := range jsonErrors {
jerr.Meta["orign"] = "Typescript-CLI"
jerr.Meta[ERROR_HAS_BEEN_DISPLAYED] = hasBeenDisplayed
errs = append(errs, jerr)
}

err = errors.Join(errs...)
c.DebugLogger.Println("Error file contained ", len(jsonErrors), " errors: ", err)
return err, nil
return errors.Join(errs...), nil
}

c.DebugLogger.Println("The file didn't contain any errors")
return nil, errors.New("no errorrs were sent thought the error file")
return nil, ErrIPCNoDataSent
}

func (c *CLI) Execute(proxyInfo *proxy.ProxyInfo, passThroughArgs []string) error {
Expand Down
120 changes: 120 additions & 0 deletions cliv2/internal/cliv2/cliv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"testing"
"time"

"github.com/snyk/error-catalog-golang-public/snyk_errors"
"github.com/snyk/go-application-framework/pkg/app"
"github.com/snyk/go-application-framework/pkg/configuration"
"github.com/snyk/go-application-framework/pkg/local_workflows/output_workflow"
Expand Down Expand Up @@ -587,3 +588,122 @@ func Test_GetErrorDisplayStatus(t *testing.T) {
})
}
}

func Test_GetErrorFromFile(t *testing.T) {
// returns an ExitError instances with the specified exit code
getExitError := func(exitCode int) error {
var err error
command := fmt.Sprintf("exit %d", exitCode)
if runtime.GOOS == "windows" {
cmd := exec.Command("cmd", "/C", command)
err = cmd.Run()
} else {
cmd := exec.Command("sh", "-c", command)
err = cmd.Run()
}

return err
}

// Setup
jsonAPIErrorBytes := []byte(`{"jsonapi":{"version":"1.0"},"errors":[{"id":"1","links":{"about":"https://docs.snyk.io/scan-with-snyk/error-catalog#snyk-os-7001"},"status":"504","code":"SNYK-OS-7001","title":"Request to Snyk API timeout","detail":"connection is timedout","meta":{"links":["https://status.snyk.io/"],"isErrorCatalogError":true,"classification":"UNEXPECTED"}}]}`)
validFilePath := path.Join(t.TempDir(), "ipc-err-file")
err := os.WriteFile(validFilePath, jsonAPIErrorBytes, 0664)
assert.Nil(t, err)

noErrorBytes := []byte(`{"jsonapi":{"version":"1.0"},"errors":[]}`)
noErrorsFilePath := path.Join(t.TempDir(), "no-err-file")
err = os.WriteFile(noErrorsFilePath, noErrorBytes, 0664)
assert.Nil(t, err)

notJsonBytes := []byte(`this is not a valid json`)
invalidFilePath := path.Join(t.TempDir(), "invalid-err-file")
err = os.WriteFile(invalidFilePath, notJsonBytes, 0664)
assert.Nil(t, err)

notFoundFilePath := path.Join(t.TempDir(), "not-found-file")

jsonApiError, err := snyk_errors.FromJSONAPIErrorBytes(jsonAPIErrorBytes)
assert.Nil(t, err)
jsonAPIError := jsonApiError[0]

config := configuration.NewWithOpts(configuration.WithAutomaticEnv())

t.Run("does not retrieve errors for exit code 0", func(t *testing.T) {
exitCodeErr := getExitError(0)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, validFilePath, config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCNotNeeded)
})

t.Run("does not retrieve errors for exit code 1", func(t *testing.T) {
exitCodeErr := getExitError(1)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, validFilePath, config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCNotNeeded)
})

t.Run("does not retrieve errors if the IPC was not used", func(t *testing.T) {
exitCodeErr := getExitError(2)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, notFoundFilePath, config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCNoDataSent)
})

t.Run("does not retrieve errors if the file cannot be read", func(t *testing.T) {
exitCodeErr := getExitError(2)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, t.TempDir(), config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCFailedToRead)
})

t.Run("does not retrieve errors if the IPC data cannot be deserialized", func(t *testing.T) {
exitCodeErr := getExitError(2)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, invalidFilePath, config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCFailedToDeserialize)
})

t.Run("does not retrieve errors if the IPC data cannot be deserialized", func(t *testing.T) {
exitCodeErr := getExitError(2)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, invalidFilePath, config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCFailedToDeserialize)
})

t.Run("does not retrieve errors if the IPC didnt send any errors", func(t *testing.T) {
exitCodeErr := getExitError(2)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, noErrorsFilePath, config)

assert.Nil(t, sentErr)
assert.ErrorIs(t, err, cliv2.ErrIPCNoDataSent)
})

t.Run("retrieves errors for exit code 2", func(t *testing.T) {
exitCodeErr := getExitError(2)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, validFilePath, config)

snykErr := snyk_errors.Error{}
assert.ErrorIs(t, sentErr, exitCodeErr)
assert.ErrorAs(t, sentErr, &snykErr)
assert.Equal(t, snykErr.ErrorCode, jsonAPIError.ErrorCode)
assert.Nil(t, err)
})

t.Run("retrieves errors for exit code 44", func(t *testing.T) {
exitCodeErr := getExitError(44)
sentErr, err := cliv2.GetErrorFromFile(exitCodeErr, validFilePath, config)

snykErr := snyk_errors.Error{}
assert.ErrorIs(t, sentErr, exitCodeErr)
assert.ErrorAs(t, sentErr, &snykErr)
assert.Equal(t, snykErr.ErrorCode, jsonAPIError.ErrorCode)
assert.Nil(t, err)
})
}
Loading