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

#66: add unit test #67

Merged
merged 2 commits into from
Jan 16, 2024
Merged
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
77 changes: 50 additions & 27 deletions cmd/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,66 @@ const (
cleanModCacheFlag = "modcache"
)

var builtinActions = []Action{
{A: "build", B: buildAction},
{A: "clean", B: cleanAction},
{A: "test", B: testAction},
}

type (
Execution func(cmd *cobra.Command, args ...string) error
Action lo.Tuple2[string, Execution]
)

func builtinActions() []Action {
return []Action{
{A: "build", B: buildAction},
{A: "clean", B: cleanAction},
{A: "test", B: testAction},
{A: "after_test", B: coverReport},
}
}

func beforeExecution(cmd *cobra.Command, arg string) error {
if action, ok := lo.Find(builtinActions(), func(action Action) bool {
return action.A == fmt.Sprintf("before_%s", arg)
}); ok {
return action.B(cmd, arg)
}
return nil
}

func afterExecution(cmd *cobra.Command, arg string) error {
if action, ok := lo.Find(builtinActions(), func(action Action) bool {
return action.A == fmt.Sprintf("after_%s", arg)
}); ok {
return action.B(cmd, arg)
}
return nil
}

func execute(cmd *cobra.Command, arg string) error {
err := beforeExecution(cmd, arg)
if err != nil {
return err
}
msg := fmt.Sprintf("Start %s project", arg)
color.Cyan("%-20s ...... \n", msg)
if plugin, ok := lo.Find(internal.CurProject().Plugins(), func(plugin internal.Plugin) bool {
return plugin.Alias == arg
}); ok {
if err := plugin.Execute(); err != nil {
return err
}
return nil
} else if action, ok := lo.Find(builtinActions, func(action Action) bool {
err = plugin.Execute()
} else if action, ok := lo.Find(builtinActions(), func(action Action) bool {
return action.A == arg
}); ok {
return action.B(cmd, arg)
err = action.B(cmd, arg)
} else {
return fmt.Errorf("can not find command %s", arg)
}
if err == nil {
return afterExecution(cmd, arg)
}
return fmt.Errorf("Can not find command %s", arg)
return nil
}

func validBuilderArgs() []string {
builtIn := lo.Map(builtinActions, func(action Action, _ int) string {
builtIn := lo.Map(lo.Filter(builtinActions(), func(item Action, _ int) bool {
return !strings.Contains(item.A, "_")
}), func(action Action, _ int) string {
return action.A
})
lo.ForEach(internal.CurProject().Plugins(), func(item internal.Plugin, _ int) {
Expand Down Expand Up @@ -115,25 +144,19 @@ func cleanAction(_ *cobra.Command, _ ...string) error {
func testAction(_ *cobra.Command, args ...string) error {
coverProfile := fmt.Sprintf("-coverprofile=%s/cover.out", internal.CurProject().Target())
testCmd := exec.Command("go", []string{"test", "-v", coverProfile, "./..."}...) //nolint
err := shared.StreamCmdOutput(testCmd, fmt.Sprintf("%s/test.log", internal.CurProject().Target()))
if err != nil {
return err
}
_, err = exec.Command("go", []string{"tool", "cover", fmt.Sprintf("-html=%s/cover.out", internal.CurProject().Target()), fmt.Sprintf("-o=%s/cover.html", internal.CurProject().Target())}...).CombinedOutput() //nolint
if err == nil {
fmt.Printf("Test log is generated at %s/test.log \n", internal.CurProject().Target())
}
reportAction(nil, args...)
return nil
return shared.StreamCmdOutput(testCmd, fmt.Sprintf("%s/test.log", internal.CurProject().Target()))
}

func reportAction(_ *cobra.Command, _ ...string) {
func coverReport(_ *cobra.Command, _ ...string) error {
target := internal.CurProject().Target()
if _, err := os.Stat(filepath.Join(target, "cover.out")); err == nil {
_, err := os.Stat(filepath.Join(target, "cover.out"))
if err == nil {
if _, err = exec.Command("go", []string{"tool", "cover", fmt.Sprintf("-html=%s/cover.out", target), fmt.Sprintf("-o=%s/cover.html", target)}...).CombinedOutput(); err == nil { //nolint
fmt.Printf("Coverage report is generated at %s/cover.html \n", target)
return nil
} else {
color.Red("Failed to generate coverage report")
return fmt.Errorf(color.RedString("Failed to generate coverage report %s", err.Error()))
}
}
return fmt.Errorf(color.RedString("Failed to generate coverage report %s", err.Error()))
}
22 changes: 21 additions & 1 deletion cmd/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/suite"
"os"
"path/filepath"
"strings"
"testing"
)

Expand All @@ -29,9 +30,28 @@ func (suite *ActionTestSuite) SetupSuite() {
// os.Remove(suite.binary)
//}

func (suite *ActionTestSuite) TestBuildClean() {
func (suite *ActionTestSuite) TestActionBuild() {
err := buildAction(nil)
assert.NoError(suite.T(), err)
_, err = os.Stat(suite.binary)
assert.NoError(suite.T(), err)
}

func (suite *ActionTestSuite) TestBeforeExecution() {
actions := lo.Filter(builtinActions(), func(item Action, _ int) bool {
return !strings.Contains(item.A, "_")
})
args := lo.Map(actions, func(item Action, _ int) string {
return item.A
})
for _, arg := range args {
assert.NoError(suite.T(), beforeExecution(nil, arg))
}
}

func (suite *ActionTestSuite) TestBuiltInActions() {
assert.Equal(suite.T(), 4, len(builtinActions()))
assert.Equal(suite.T(), []string{"build", "clean", "test", "after_test"}, lo.Map(builtinActions(), func(item Action, index int) string {
return item.A
}))
}
17 changes: 9 additions & 8 deletions cmd/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ const resourceDir = "resources"

// builderCmd represents the base command when called without any subcommands
var builderCmd = &cobra.Command{
Use: "gob",
Short: "Go project boot",
Long: `Supply most frequently used tool and best practices for go project development`,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return validBuilderArgs(), cobra.ShellCompDirectiveError
},
Use: "gob",
Short: "Go project boot",
Long: `Go pluggable toolchain and best practice`,
ValidArgs: validBuilderArgs(),
Args: func(cmd *cobra.Command, args []string) error {
if !lo.Every(validBuilderArgs(), args) {
return fmt.Errorf("valid args are : %s", validBuilderArgs())
return fmt.Errorf(color.RedString("valid args are : %s", validBuilderArgs()))
}
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
return fmt.Errorf(color.RedString(err.Error()))
}
return cobra.MinimumNArgs(1)(cmd, args)
return nil
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
internal.CurProject().Validate()
Expand Down
57 changes: 55 additions & 2 deletions cmd/builder_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package cmd

import (
"github.com/fatih/color"
"github.com/kcmvp/gob/internal"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"os"
"path/filepath"
"strings"
"testing"
)

Expand Down Expand Up @@ -34,6 +36,59 @@ func (suite *BuilderTestSuit) TearDownTest() {
os.RemoveAll(suite.testDir)
}

func (suite *BuilderTestSuit) TestValidArgs() {
assert.Equal(suite.T(), []string{"build", "clean", "test", "lint"}, builderCmd.ValidArgs)
}

func (suite *BuilderTestSuit) TestArgs() {
tests := []struct {
name string
args []string
wantErr bool
}{
{
name: "not in valid args list",
args: []string{"def"},
wantErr: true,
},
{
name: "partial valid args",
args: []string{"test", "def"},
wantErr: true,
},
{
name: "no args",
args: []string{},
wantErr: true,
},
{
name: "empty args",
args: []string{""},
wantErr: true,
},
{
name: "positive case",
args: []string{"clean", "test"},
wantErr: false,
},
}
for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) {
err := builderCmd.Args(nil, test.args)
assert.True(t, test.wantErr == (err != nil))
})
}

}

func (suite *BuilderTestSuit) TestBuild() {
builderCmd.SetArgs([]string{"cd"})
err := builderCmd.Execute()
assert.Error(suite.T(), err)
assert.True(suite.T(), strings.Contains(err.Error(), color.RedString("")))

}

func (suite *BuilderTestSuit) TestPersistentPreRun() {
builderCmd.PersistentPreRun(nil, nil)
hooks := lo.MapToSlice(internal.HookScripts(), func(key string, _ string) string {
Expand All @@ -48,7 +103,6 @@ func (suite *BuilderTestSuit) TestPersistentPreRun() {
_, err := os.Stat(filepath.Join(internal.CurProject().HookDir(), hook))
assert.NoError(suite.T(), err)
}

}

func (suite *BuilderTestSuit) TestBuiltinPlugins() {
Expand All @@ -70,5 +124,4 @@ func (suite *BuilderTestSuit) TestBuiltinPlugins() {
assert.Equal(suite.T(), "gotestsum", plugin.Name())
assert.Equal(suite.T(), "gotest.tools/gotestsum", plugin.Module())
assert.Equal(suite.T(), "test", plugin.Alias)

}
7 changes: 7 additions & 0 deletions internal/hook_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package internal

import (
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"os"
Expand Down Expand Up @@ -34,6 +35,12 @@ func (suite *GitHookTestSuite) TestSetupHook() {
CurProject().SetupHooks(true)
info1, err := os.Stat(filepath.Join(suite.testDir, "gob.yaml"))
assert.NoError(suite.T(), err)
executions := CurProject().Executions()
assert.Equal(suite.T(), 3, len(executions))
rs := lo.Every([]string{"commit-msg-hook", "pre-commit-hook", "pre-push-hook"}, lo.Map(executions, func(item Execution, _ int) string {
return item.CmdKey
}))
assert.True(suite.T(), rs)
hook := CurProject().GitHook()
assert.NotEmpty(suite.T(), hook.CommitMsg)
assert.Equal(suite.T(), []string([]string{"lint", "test"}), hook.PreCommit)
Expand Down
9 changes: 6 additions & 3 deletions internal/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import (
"sync"
)

const pluginCfgKey = "plugins"
const (
pluginCfgKey = "plugins"
defaultCfgKey = "_default_"
)

var (
project Project
Expand All @@ -28,7 +31,7 @@ type Project struct {
root string
module string
deps []string
cfg sync.Map
cfg sync.Map // store all the configuration
}

func TestCallee() (bool, string) {
Expand All @@ -55,7 +58,7 @@ func TestCallee() (bool, string) {

func (project *Project) config() *viper.Viper {
testEnv, file := TestCallee()
key := lo.If(testEnv, file).Else("_default_")
key := lo.If(testEnv, file).Else(defaultCfgKey)
obj, ok := project.cfg.Load(key)
if ok {
return obj.(*viper.Viper)
Expand Down