Skip to content

Commit

Permalink
Merge pull request #162 from danielsuguimoto/run_scripts
Browse files Browse the repository at this point in the history
feat(run): added available scripts to run command
  • Loading branch information
fabriciojs authored Oct 14, 2020
2 parents 097a5f9 + a599469 commit 2b7adcd
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 19 deletions.
21 changes: 16 additions & 5 deletions cmd/parser/fake_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (

// FakeParser implements all fake behaviors for using parser in tests.
type FakeParser struct {
CalledAddLookupPath bool
TargetFiles []string
CalledParse bool
MockParsedCommands []builder.Command
MockParseError error
CalledAddLookupPath bool
TargetFiles []string
CalledParse bool
CalledParseAvailableScripts bool
MockParsedCommands []builder.Command
MockParseError error
MockScripts []string
MockParseAvailableScriptsError error
}

// AddLookupPath implements fake AddLookupPath behavior
Expand All @@ -27,3 +30,11 @@ func (f *FakeParser) Parse(script string) (commands []builder.Command, err error
err = f.MockParseError
return
}

// ParseAvailableScripts implements fake ParseAvailableScripts behavior
func (f *FakeParser) ParseAvailableScripts() (scripts []string, err error) {
f.CalledParseAvailableScripts = true
scripts = f.MockScripts
err = f.MockParseAvailableScriptsError
return
}
21 changes: 18 additions & 3 deletions cmd/parser/fake_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,33 @@ func TestFakeParser(t *testing.T) {
commands, _ := f.Parse("script")

if !f.CalledParse || len(commands) != 1 {
t.Error("failed to use mocked Parse function more then once on FakeParser")
t.Error("failed to use mocked Parse function on FakeParser")
}

f.MockScripts = []string{"script"}

scripts, _ := f.ParseAvailableScripts()

if !f.CalledParseAvailableScripts || len(scripts) != 1 || scripts[0] != "script" {
t.Error("failed to use mocked ParseAvailableScripts function on FakeParser")
}
}

func TestFakeFailedParser(t *testing.T) {
f := &FakeParser{
MockParseError: errors.New("error"),
MockParseError: errors.New("parser error"),
MockParseAvailableScriptsError: errors.New("get scripts error"),
}

_, err := f.Parse("script")

if !f.CalledParse || err == nil {
t.Error("failed to use mocked failing Parse function more then once on FakeParser")
t.Error("failed to use mocked failing Parse function on FakeParser")
}

_, err = f.ParseAvailableScripts()

if !f.CalledParseAvailableScripts || err == nil {
t.Error("failed to use mocked failing ParseAvailableScripts function on FakeParser")
}
}
38 changes: 37 additions & 1 deletion cmd/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import (
"kool-dev/kool/cmd/builder"
"os"
"path"
"sort"
)

// Parser defines the functions required for handling kool.yml files.
type Parser interface {
AddLookupPath(string) error
Parse(string) ([]builder.Command, error)
ParseAvailableScripts() ([]string, error)
}

// DefaultParser implements all default behavior for using kool.yml files.
type DefaultParser struct {
targetFiles []string
}

// NewParser initialises a Parser to be used for handling kool.yml scripts.
// NewParser initializes a Parser to be used for handling kool.yml scripts.
func NewParser() Parser {
return &DefaultParser{}
}
Expand Down Expand Up @@ -81,3 +83,37 @@ func (p *DefaultParser) Parse(script string) (commands []builder.Command, err er
}
return
}

// ParseAvailableScripts parse all available scripts
func (p *DefaultParser) ParseAvailableScripts() (scripts []string, err error) {
var (
koolFile string
parsedFile *KoolYaml
foundScripts map[string]bool
)

if len(p.targetFiles) == 0 {
err = errors.New("kool.yml not found")
return
}

foundScripts = make(map[string]bool)

for _, koolFile = range p.targetFiles {
if parsedFile, err = ParseKoolYaml(koolFile); err != nil {
return
}

for script := range parsedFile.Scripts {
if !foundScripts[script] {
scripts = append(scripts, script)
}

foundScripts[script] = true
}
}

sort.Strings(scripts)

return
}
69 changes: 64 additions & 5 deletions cmd/parser/parser_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package parser

import (
"io/ioutil"
"kool-dev/kool/cmd/builder"
"os"
"path"
"testing"
Expand Down Expand Up @@ -29,12 +29,71 @@ func TestParserAddLooupPath(t *testing.T) {
return
}

if err = ioutil.WriteFile(path.Join(tmpDir, "kool.yml"), []byte(KoolYmlOK), os.ModePerm); err != nil {
t.Fatalf("failed creating temp file; error: %s", err)
workDir, _ := os.Getwd()
if err = p.AddLookupPath(path.Join(workDir, "testing_files")); err != nil {
t.Errorf("unexpected error; error: %s", err)
return
}
}

if err = p.AddLookupPath(tmpDir); err != nil {
func TestParserParse(t *testing.T) {
var (
p Parser = NewParser()
commands []builder.Command
err error
)

if _, err = p.Parse("testing"); err == nil {
t.Error("expecting 'kool.yml not found' error, got none")
}

if err != nil && err.Error() != "kool.yml not found" {
t.Errorf("expecting error 'kool.yml not found', got '%s'", err.Error())
}

workDir, _ := os.Getwd()
_ = p.AddLookupPath(path.Join(workDir, "testing_files"))

if commands, err = p.Parse("testing"); err != nil {
t.Errorf("unexpected error; error: %s", err)
return
}

if len(commands) != 1 || commands[0].String() != "echo testing" {
t.Error("failed to parse testing kool.yml")
}

if commands, err = p.Parse("invalid"); err != nil {
t.Errorf("unexpected error; error: %s", err)
}

if len(commands) > 0 {
t.Error("should not find scripts")
}
}

func TestParserParseAvailableScripts(t *testing.T) {
var (
p Parser = NewParser()
scripts []string
err error
)

if _, err = p.ParseAvailableScripts(); err == nil {
t.Error("expecting 'kool.yml not found' error, got none")
}

if err != nil && err.Error() != "kool.yml not found" {
t.Errorf("expecting error 'kool.yml not found', got '%s'", err.Error())
}

workDir, _ := os.Getwd()
_ = p.AddLookupPath(path.Join(workDir, "testing_files"))

if scripts, err = p.ParseAvailableScripts(); err != nil {
t.Errorf("unexpected error; error: %s", err)
}

if len(scripts) != 1 || scripts[0] != "testing" {
t.Error("failed to get all scripts from kool.yml")
}
}
2 changes: 2 additions & 0 deletions cmd/parser/testing_files/kool.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scripts:
testing: "echo testing"
47 changes: 42 additions & 5 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"kool-dev/kool/cmd/parser"
"kool-dev/kool/environment"
"path"
"strings"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -50,11 +51,6 @@ func (r *KoolRun) Execute(originalArgs []string) (err error) {
args []string
)

// look for kool.yml on current working directory
_ = r.parser.AddLookupPath(r.envStorage.Get("PWD"))
// look for kool.yml on kool folder within user home directory
_ = r.parser.AddLookupPath(path.Join(r.envStorage.Get("HOME"), "kool"))

script = originalArgs[0]
args = originalArgs[1:]

Expand Down Expand Up @@ -100,5 +96,46 @@ func NewRunCommand(run *KoolRun) (runCmd *cobra.Command) {

// after a non-flag arg, stop parsing flags
runCmd.Flags().SetInterspersed(false)

// look for kool.yml on current working directory
_ = run.parser.AddLookupPath(run.envStorage.Get("PWD"))
// look for kool.yml on kool folder within user home directory
_ = run.parser.AddLookupPath(path.Join(run.envStorage.Get("HOME"), "kool"))

var (
usageTempl string
err error
)

if usageTempl, err = getRunUsageTemplate(run, runCmd); err == nil {
runCmd.SetUsageTemplate(usageTempl)
} else if run.envStorage.IsTrue("KOOL_VERBOSE") {
run.Println("$ got an error trying to add available scripts to command usage template; error:", err.Error())
}

return
}

func getRunUsageTemplate(run *KoolRun, cmd *cobra.Command) (templ string, err error) {
var (
sb strings.Builder
scripts []string
)

if scripts, err = run.parser.ParseAvailableScripts(); err != nil {
return
}

sb.WriteString(cmd.UsageTemplate())
sb.WriteString("\n")
sb.WriteString("Available Scripts:\n")

for _, script := range scripts {
sb.WriteString(" ")
sb.WriteString(script)
sb.WriteString("\n")
}

templ = sb.String()
return
}
39 changes: 39 additions & 0 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"kool-dev/kool/cmd/parser"
"kool-dev/kool/cmd/shell"
"kool-dev/kool/environment"
"strings"
"testing"
)

Expand Down Expand Up @@ -227,3 +228,41 @@ func TestNewRunCommandWithArguments(t *testing.T) {
t.Error("did not call AppendArgs properly for parsed command")
}
}

func TestNewRunCommandUsageTemplate(t *testing.T) {
f := newFakeKoolRun([]builder.Command{}, nil)
f.parser.(*parser.FakeParser).MockScripts = []string{"testing_script"}
cmd := NewRunCommand(f)

usageTemplate := cmd.UsageTemplate()

if !strings.Contains(usageTemplate, "testing_script") {
t.Error("did not find testing_script as available script on usage text")
}
}

func TestNewRunCommandFailingUsageTemplate(t *testing.T) {
f := newFakeKoolRun([]builder.Command{}, nil)
f.parser.(*parser.FakeParser).MockScripts = []string{"testing_script"}
f.parser.(*parser.FakeParser).MockParseAvailableScriptsError = errors.New("error parse avaliable scripts")
f.envStorage.(*environment.FakeEnvStorage).Envs["KOOL_VERBOSE"] = "1"

cmd := NewRunCommand(f)

usageTemplate := cmd.UsageTemplate()

if strings.Contains(usageTemplate, "testing_script") {
t.Error("should not find testing_script as available script on usage text due to error on parsing scripts")
}

if !f.out.(*shell.FakeOutputWriter).CalledPrintln {
t.Error("did not call Println to output error on getting available scripts when KOOL_VERBOSE is true")
}

expected := "$ got an error trying to add available scripts to command usage template; error: error parse avaliable scripts"
output := strings.TrimSpace(fmt.Sprintln(f.out.(*shell.FakeOutputWriter).Out...))

if expected != output {
t.Errorf("expecting message '%s', got '%s'", expected, output)
}
}

0 comments on commit 2b7adcd

Please sign in to comment.