Skip to content

Commit

Permalink
Adding result.view command
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanoj3 committed Aug 18, 2019
1 parent b19c5ab commit bf7a11f
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 0 deletions.
6 changes: 6 additions & 0 deletions cmd/dirstalk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ func createCommand(logger *logrus.Logger) (*cobra.Command, error) {
return nil, errors.Wrap(err, "failed to create scan command")
}

resultViewCmd, err := cmd.NewResultViewCommand(logger.Out)
if err != nil {
return nil, errors.Wrap(err, "failed to create result-view command")
}

dirStalkCmd.AddCommand(scanCmd)
dirStalkCmd.AddCommand(resultViewCmd)
dirStalkCmd.AddCommand(cmd.NewGenerateDictionaryCommand(logger.Out))
dirStalkCmd.AddCommand(cmd.NewVersionCommand(logger.Out))

Expand Down
7 changes: 7 additions & 0 deletions functional-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,10 @@ assert_not_contains "$SCAN_RESULT" "error" "no error is expected when priting sc
DICTIONARY_GENERATE_RESULT=$(./dist/dirstalk dictionary.generate resources/tests 2>&1 || true);
assert_contains "$DICTIONARY_GENERATE_RESULT" "dictionary.txt" "dictionary generation should contains a file in the folder"
assert_not_contains "$DICTIONARY_GENERATE_RESULT" "error" "no error is expected when generating a dictionary successfully"

RESULT_VIEW_RESULT=$(./dist/dirstalk result.view -r resources/tests/out.txt 2>&1 || true);
assert_contains "$RESULT_VIEW_RESULT" "├── adview" "result output should contain tree output"
assert_contains "$RESULT_VIEW_RESULT" "├── partners" "result output should contain tree output"
assert_contains "$RESULT_VIEW_RESULT" "│ └── terms" "result output should contain tree output"
assert_contains "$RESULT_VIEW_RESULT" "└── s" "result output should contain tree output"
assert_not_contains "$RESULT_VIEW_RESULT" "error" "no error is expected when displaying a result"
4 changes: 4 additions & 0 deletions pkg/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ const (
flagOutput = "out"
flagOutputShort = "o"
flagAbsolutePathOnly = "absolute-only"

// Result view flags
flagResultFile = "result-file"
flagResultFileShort = "r"
)
51 changes: 51 additions & 0 deletions pkg/cmd/result_view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cmd

import (
"io"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/stefanoj3/dirstalk/pkg/result"
"github.com/stefanoj3/dirstalk/pkg/scan/summarizer/tree"
)

func NewResultViewCommand(out io.Writer) (*cobra.Command, error) {
cmd := &cobra.Command{
Use: "result.view",
Short: "Read a scan output file and render the folder tree",
RunE: buildResultViewCmd(out),
}

cmd.Flags().StringP(
flagResultFile,
flagResultFileShort,
"",
"result file to read",
)
err := cmd.MarkFlagFilename(flagResultFile)
if err != nil {
return nil, err
}

err = cmd.MarkFlagRequired(flagResultFile)
if err != nil {
return nil, err
}

return cmd, nil
}

func buildResultViewCmd(out io.Writer) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
resultFilePath := cmd.Flag(flagResultFile).Value.String()

results, err := result.LoadResultsFromFile(resultFilePath)
if err != nil {
return errors.Wrapf(err, "failed to load results from %s", resultFilePath)
}

tree.NewResultTreePrinter().Print(results, out)

return nil
}
}
55 changes: 55 additions & 0 deletions pkg/cmd/result_view_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package cmd_test

import (
"testing"

"github.com/stefanoj3/dirstalk/pkg/common/test"
"github.com/stretchr/testify/assert"
)

func TestResultViewShouldErrWhenCalledWithoutResultFlag(t *testing.T) {
logger, _ := test.NewLogger()

c, err := createCommand(logger)
assert.NoError(t, err)
assert.NotNil(t, c)

_, _, err = executeCommand(c, "result.view")
assert.Error(t, err)

assert.Contains(t, err.Error(), "result-file")
assert.Contains(t, err.Error(), "not set")
}

func TestResultViewShouldErrWhenCalledWithInvalidPath(t *testing.T) {
logger, _ := test.NewLogger()

c, err := createCommand(logger)
assert.NoError(t, err)
assert.NotNil(t, c)

_, _, err = executeCommand(c, "result.view", "-r", "/root/123/abc")
assert.Error(t, err)

assert.Contains(t, err.Error(), "failed to load results from")
}

func TestResultView(t *testing.T) {
logger, loggerBuffer := test.NewLogger()

c, err := createCommand(logger)
assert.NoError(t, err)
assert.NotNil(t, c)

_, _, err = executeCommand(c, "result.view", "-r", "testdata/out.txt")
assert.NoError(t, err)

expected := `/
├── adview
├── partners
│ └── terms
└── s
`

assert.Contains(t, loggerBuffer.String(), expected)
}
6 changes: 6 additions & 0 deletions pkg/cmd/root_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ func createCommand(logger *logrus.Logger) (*cobra.Command, error) {
return nil, errors.Wrap(err, "failed to create scan command")
}

resultViewCommand, err := cmd.NewResultViewCommand(logger.Out)
if err != nil {
return nil, errors.Wrap(err, "failed to create result.view command")
}

dirStalkCmd.AddCommand(scanCmd)
dirStalkCmd.AddCommand(resultViewCommand)
dirStalkCmd.AddCommand(cmd.NewGenerateDictionaryCommand(logger.Out))
dirStalkCmd.AddCommand(cmd.NewVersionCommand(logger.Out))

Expand Down
4 changes: 4 additions & 0 deletions pkg/cmd/testdata/out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"Target":{"Path":"partners","Method":"GET","Depth":3},"StatusCode":200,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/partners","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"s","Method":"GET","Depth":3},"StatusCode":400,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/s","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"adview","Method":"GET","Depth":3},"StatusCode":204,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/adview","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"partners/terms","Method":"GET","Depth":2},"StatusCode":200,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/partners/terms","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
50 changes: 50 additions & 0 deletions pkg/result/load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package result

import (
"bufio"
"encoding/json"
"os"

"github.com/pkg/errors"
"github.com/stefanoj3/dirstalk/pkg/scan"
)

func LoadResultsFromFile(resultFilePath string) ([]scan.Result, error) {
file, err := os.Open(resultFilePath) // #nosec
if err != nil {
return nil, errors.Wrapf(err, "failed to open %s", resultFilePath)
}

defer file.Close()

fileInfo, err := file.Stat()
if err != nil {
return nil, errors.Wrapf(err, "failed to read properties of %s", resultFilePath)
}

if fileInfo.IsDir() {
return nil, errors.Errorf("`%s` is a directory, you need to specify a valid result file", resultFilePath)
}

fileScanner := bufio.NewScanner(file)

lineCounter := 0
results := make([]scan.Result, 0, 10)
for fileScanner.Scan() {
lineCounter++

r := scan.Result{}
err := json.Unmarshal(fileScanner.Bytes(), &r)
if err != nil {
return nil, errors.Wrapf(err, "unable to read line %d", lineCounter)
}

results = append(results, r)
}

if err := fileScanner.Err(); err != nil {
return nil, errors.Wrap(err, "an error occurred while reading the result file")
}

return results, nil
}
15 changes: 15 additions & 0 deletions pkg/result/load_integration_darwin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package result_test

import (
"testing"

"github.com/stefanoj3/dirstalk/pkg/result"
"github.com/stretchr/testify/assert"
)

func TestLoadResultsFromFileShouldErrForInvalidPath(t *testing.T) {
_, err := result.LoadResultsFromFile("/root/123/abc")
assert.Error(t, err)

assert.Contains(t, err.Error(), "no such file or directory")
}
15 changes: 15 additions & 0 deletions pkg/result/load_integration_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package result_test

import (
"testing"

"github.com/stefanoj3/dirstalk/pkg/result"
"github.com/stretchr/testify/assert"
)

func TestLoadResultsFromFileShouldErrForInvalidPath(t *testing.T) {
_, err := result.LoadResultsFromFile("/root/123/abc")
assert.Error(t, err)

assert.Contains(t, err.Error(), "permission denied")
}
74 changes: 74 additions & 0 deletions pkg/result/load_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package result_test

import (
"net/url"
"testing"

"github.com/stefanoj3/dirstalk/pkg/result"
"github.com/stefanoj3/dirstalk/pkg/scan"
"github.com/stretchr/testify/assert"
)

func TestLoadResultsFromFile(t *testing.T) {
results, err := result.LoadResultsFromFile("testdata/out.txt")
assert.NoError(t, err)

expectedResults := []scan.Result{
scan.Result{
Target: scan.Target{Path: "partners", Method: "GET", Depth: 3},
StatusCode: 200,
URL: url.URL{
Scheme: "https",
User: (*url.Userinfo)(nil),
Host: "www.brucewillisdiesinarmageddon.co.de",
Path: "/partners",
},
},
scan.Result{
Target: scan.Target{Path: "s", Method: "GET", Depth: 3},
StatusCode: 400,
URL: url.URL{
Scheme: "https",
User: (*url.Userinfo)(nil),
Host: "www.brucewillisdiesinarmageddon.co.de",
Path: "/s",
},
},
scan.Result{
Target: scan.Target{Path: "adview", Method: "GET", Depth: 3},
StatusCode: 204,
URL: url.URL{
Scheme: "https",
User: (*url.Userinfo)(nil),
Host: "www.brucewillisdiesinarmageddon.co.de",
Path: "/adview",
},
},
scan.Result{
Target: scan.Target{Path: "partners/terms", Method: "GET", Depth: 2},
StatusCode: 200,
URL: url.URL{
Scheme: "https",
User: (*url.Userinfo)(nil),
Host: "www.brucewillisdiesinarmageddon.co.de",
Path: "/partners/terms",
},
},
}

assert.Equal(t, expectedResults, results)
}

func TestLoadResultsFromFileShouldErrForDirectories(t *testing.T) {
_, err := result.LoadResultsFromFile("testdata/")
assert.Error(t, err)

assert.Contains(t, err.Error(), "is a directory")
}

func TestLoadResultsFromFileShouldErrForInvalidFileFormat(t *testing.T) {
_, err := result.LoadResultsFromFile("testdata/invalidout.txt")
assert.Error(t, err)

assert.Contains(t, err.Error(), "unable to read line")
}
1 change: 1 addition & 0 deletions pkg/result/testdata/invalidout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{omg/
4 changes: 4 additions & 0 deletions pkg/result/testdata/out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"Target":{"Path":"partners","Method":"GET","Depth":3},"StatusCode":200,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/partners","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"s","Method":"GET","Depth":3},"StatusCode":400,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/s","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"adview","Method":"GET","Depth":3},"StatusCode":204,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/adview","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"partners/terms","Method":"GET","Depth":2},"StatusCode":200,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/partners/terms","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
4 changes: 4 additions & 0 deletions resources/tests/out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"Target":{"Path":"partners","Method":"GET","Depth":3},"StatusCode":200,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/partners","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"s","Method":"GET","Depth":3},"StatusCode":400,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/s","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"adview","Method":"GET","Depth":3},"StatusCode":204,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/adview","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}
{"Target":{"Path":"partners/terms","Method":"GET","Depth":2},"StatusCode":200,"URL":{"Scheme":"https","Opaque":"","User":null,"Host":"www.brucewillisdiesinarmageddon.co.de","Path":"/partners/terms","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":""}}

0 comments on commit bf7a11f

Please sign in to comment.