Skip to content

Commit

Permalink
Added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
breathbath committed Mar 12, 2021
1 parent 943bf7e commit 1c38bc4
Show file tree
Hide file tree
Showing 7 changed files with 596 additions and 64 deletions.
7 changes: 7 additions & 0 deletions internal/pkg/config/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/fatih/color"
)

const maxPromptIterations = 100

type PromptReader interface {
ReadString() (string, error)
ReadPassword() (string, error)
Expand Down Expand Up @@ -40,6 +42,7 @@ func PromptRequiredValues(
}

err = missedReqP.Validate(missedReqP.Field, readValue)
promptCount := 0
for err != nil {
readValue, err = promptValue(missedReqP, promptReader)
if err != nil {
Expand All @@ -50,6 +53,10 @@ func PromptRequiredValues(
if err != nil {
color.Red(err.Error())
}
promptCount++
if promptCount > maxPromptIterations {
return fmt.Errorf("max prompt attempts %d elapsed", maxPromptIterations)
}
}
targetKV[missedReqP.Field] = readValue
}
Expand Down
7 changes: 4 additions & 3 deletions internal/pkg/controllers/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/signal"
Expand Down Expand Up @@ -224,11 +225,11 @@ func (icm *InteractiveCommandsController) processRawMessage(msg []byte) error {
var errResp models.ErrorResp
err = json.Unmarshal(msg, &errResp)
if err != nil {
logrus.Errorf("cannot recognize command output message: %s, reason: %v", string(msg), err)
return err
e := fmt.Errorf("cannot recognize command output message: %s, reason: %v", string(msg), err)
return e
}
icm.Spinner.StopError(errResp.Error())
return nil
return errResp
}

logrus.Debugf("received message: '%s'", string(msg))
Expand Down
299 changes: 299 additions & 0 deletions internal/pkg/controllers/commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
package controllers

import (
"context"
"encoding/json"
"io"
"testing"
"time"

"github.com/cloudradar-monitoring/rportcli/internal/pkg/models"
"github.com/stretchr/testify/assert"
)

type ReadChunk struct {
Output []byte
Err error
}

type ReadWriterMock struct {
itemsToRead []ReadChunk
itemReadIndex int
writtenItems []string
writeError error
isClosed bool
closeError error
}

func (rwm *ReadWriterMock) Read() (msg []byte, err error) {
item := rwm.itemsToRead[rwm.itemReadIndex]

msg = item.Output
err = item.Err

rwm.itemReadIndex++

return
}

func (rwm *ReadWriterMock) Write(inputMsg []byte) (n int, err error) {
rwm.writtenItems = append(rwm.writtenItems, string(inputMsg))
return 0, rwm.writeError
}

func (rwm *ReadWriterMock) Close() error {
rwm.isClosed = true
return rwm.closeError
}

type SpinnerMock struct {
startMsgs []string
updateMsgs []string
stopSuccessMsgs []string
stopErrorMsgs []string
}

func (sm *SpinnerMock) Start(msg string) {
sm.startMsgs = append(sm.startMsgs, msg)
}

func (sm *SpinnerMock) Update(msg string) {
sm.updateMsgs = append(sm.updateMsgs, msg)
}

func (sm *SpinnerMock) StopSuccess(msg string) {
sm.stopSuccessMsgs = append(sm.stopSuccessMsgs, msg)
}

func (sm *SpinnerMock) StopError(msg string) {
sm.stopErrorMsgs = append(sm.stopErrorMsgs, msg)
}

type JobRendererMock struct {
jobToRender *models.Job
err error
}

func (jrm *JobRendererMock) RenderJob(j *models.Job) error {
jrm.jobToRender = j
return jrm.err
}

func TestInteractiveCommandExecutionSuccess(t *testing.T) {
jobResp := models.Job{
Jid: "123",
Status: "done",
FinishedAt: time.Now(),
ClientID: "123",
Command: "ls",
Shell: "sh",
Pid: 12,
StartedAt: time.Now(),
CreatedBy: "admin",
TimeoutSec: 1,
Result: models.JobResult{
Stdout: "some out",
Stderr: "some err",
},
}
jobRespBytes, err := json.Marshal(jobResp)
assert.NoError(t, err)
if err != nil {
return
}

rw := &ReadWriterMock{
itemsToRead: []ReadChunk{
{
Output: jobRespBytes,
},
{
Err: io.EOF,
},
},
writtenItems: []string{},
isClosed: false,
}

pr := &PromptReaderMock{
ReadOutputs: []string{},
PasswordReadOutputs: []string{},
}

s := &SpinnerMock{
startMsgs: []string{},
updateMsgs: []string{},
stopSuccessMsgs: []string{},
stopErrorMsgs: []string{},
}

jr := &JobRendererMock{}

ic := &InteractiveCommandsController{
ReadWriter: rw,
PromptReader: pr,
Spinner: s,
JobRenderer: jr,
}

cids := "1235"
cmd := "cmd"
to := "1"
gi := "333"
ec := "1"
err = ic.Start(context.Background(), map[string]*string{
clientIDs: &cids,
command: &cmd,
timeout: &to,
groupIDs: &gi,
execConcurrently: &ec,
})

assert.NoError(t, err)

assert.Equal(t, pr.PasswordReadCount, 0)
assert.Equal(t, pr.ReadCount, 0)

assert.Len(t, rw.writtenItems, 1)
expectedCommandInput := `{"command":"cmd","client_ids":["1235"],"group_ids":["333"],"timeout_sec":1,"execute_concurrently":true}`
assert.Equal(t, expectedCommandInput, rw.writtenItems[0])

assert.NotNil(t, jr.jobToRender)
actualJobRenderResult, err := json.Marshal(jr.jobToRender)
assert.NoError(t, err)
assert.Equal(t, string(jobRespBytes), string(actualJobRenderResult))
assert.True(t, rw.isClosed)
assert.Len(t, s.stopErrorMsgs, 0)
assert.Len(t, s.stopSuccessMsgs, 2)
}

func TestInteractiveCommandExecutionWithPromptParams(t *testing.T) {
jobResp := models.Job{
Jid: "123",
Status: "done",
}
jobRespBytes, err := json.Marshal(jobResp)
assert.NoError(t, err)
if err != nil {
return
}

rw := &ReadWriterMock{
itemsToRead: []ReadChunk{
{
Output: jobRespBytes,
},
{
Err: io.EOF,
},
},
writtenItems: []string{},
isClosed: false,
}

pr := &PromptReaderMock{
ReadOutputs: []string{
"123",
"dir",
},
PasswordReadOutputs: []string{},
}

s := &SpinnerMock{
startMsgs: []string{},
updateMsgs: []string{},
stopSuccessMsgs: []string{},
stopErrorMsgs: []string{},
}

jr := &JobRendererMock{}

ic := &InteractiveCommandsController{
ReadWriter: rw,
PromptReader: pr,
Spinner: s,
JobRenderer: jr,
}

err = ic.Start(context.Background(), map[string]*string{})

assert.NoError(t, err)

assert.Equal(t, pr.PasswordReadCount, 0)
assert.Equal(t, pr.ReadCount, 2)

assert.Len(t, rw.writtenItems, 1)
expectedCommandInput := `{"command":"dir","client_ids":["123"],"timeout_sec":30,"execute_concurrently":false}`
assert.Equal(t, expectedCommandInput, rw.writtenItems[0])

assert.NotNil(t, jr.jobToRender)
actualJobRenderResult, err := json.Marshal(jr.jobToRender)
assert.NoError(t, err)
assert.Equal(t, string(jobRespBytes), string(actualJobRenderResult))
assert.True(t, rw.isClosed)
assert.Len(t, s.stopErrorMsgs, 0)
assert.Len(t, s.stopSuccessMsgs, 2)
}

func TestInteractiveCommandExecutionWithInvalidResponse(t *testing.T) {
resp := models.ErrorResp{
Errors: []models.Error{
{
Code: "500",
Title: "some error",
Detail: "some error detail",
},
},
}
jobRespBytes, err := json.Marshal(resp)
assert.NoError(t, err)
if err != nil {
return
}
rw := &ReadWriterMock{
itemsToRead: []ReadChunk{
{
Output: jobRespBytes,
},
{
Err: io.EOF,
},
},
writtenItems: []string{},
isClosed: false,
}

pr := &PromptReaderMock{
ReadOutputs: []string{},
PasswordReadOutputs: []string{},
}

s := &SpinnerMock{
startMsgs: []string{},
updateMsgs: []string{},
stopSuccessMsgs: []string{},
stopErrorMsgs: []string{},
}

jr := &JobRendererMock{}

ic := &InteractiveCommandsController{
ReadWriter: rw,
PromptReader: pr,
Spinner: s,
JobRenderer: jr,
}

cids := "123"
cmd := "ls"
err = ic.Start(context.Background(), map[string]*string{
clientIDs: &cids,
command: &cmd,
})

assert.Error(t, err)
if err == nil {
return
}
assert.Contains(t, err.Error(), "some error, code: 500, details: some error detail")
}
28 changes: 0 additions & 28 deletions internal/pkg/controllers/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,6 @@ import (
"github.com/stretchr/testify/assert"
)

type PromptReaderMock struct {
ReadCount int
PasswordReadCount int
ReadOutputs []string
PasswordReadOutputs []string
ErrToGive error
}

func (prm *PromptReaderMock) ReadString() (string, error) {
prm.ReadCount++

if len(prm.ReadOutputs) < prm.ReadCount {
return "", prm.ErrToGive
}

return prm.ReadOutputs[prm.ReadCount-1], prm.ErrToGive
}

func (prm *PromptReaderMock) ReadPassword() (string, error) {
prm.PasswordReadCount++

if len(prm.PasswordReadOutputs) < prm.PasswordReadCount {
return "", prm.ErrToGive
}

return prm.PasswordReadOutputs[prm.PasswordReadCount-1], prm.ErrToGive
}

func TestInitSuccess(t *testing.T) {
statusRequested := false
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
Expand Down
Loading

0 comments on commit 1c38bc4

Please sign in to comment.