add CmdStubber

This commit is contained in:
vilmibm 2020-03-11 15:52:49 -05:00
parent 8769299731
commit ed8aaa83e3
8 changed files with 140 additions and 179 deletions

View file

@ -10,6 +10,7 @@ import (
"strings"
"testing"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
)
@ -226,7 +227,7 @@ func TestIssueView(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -260,7 +261,7 @@ func TestIssueView_numberArgWithHash(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -383,7 +384,7 @@ func TestIssueView_notFound(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -430,7 +431,7 @@ func TestIssueView_urlArg(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -515,7 +516,7 @@ func TestIssueCreate_web(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -541,7 +542,7 @@ func TestIssueCreate_webTitleBody(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()

View file

@ -7,6 +7,7 @@ import (
"testing"
"github.com/cli/cli/context"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
)
@ -46,7 +47,7 @@ func TestPRCheckout_sameRepo(t *testing.T) {
return &errorStub{"exit status: 1"}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -98,7 +99,7 @@ func TestPRCheckout_urlArg(t *testing.T) {
return &errorStub{"exit status: 1"}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -147,7 +148,7 @@ func TestPRCheckout_branchArg(t *testing.T) {
return &errorStub{"exit status: 1"}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -193,10 +194,10 @@ func TestPRCheckout_existingBranch(t *testing.T) {
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
switch strings.Join(cmd.Args, " ") {
case "git show-ref --verify --quiet refs/heads/feature":
return &outputStub{}
return &test.OutputStub{}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -248,7 +249,7 @@ func TestPRCheckout_differentRepo_remoteExists(t *testing.T) {
return &errorStub{"exit status: 1"}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -300,7 +301,7 @@ func TestPRCheckout_differentRepo(t *testing.T) {
return &errorStub{"exit status 1"}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -349,10 +350,10 @@ func TestPRCheckout_differentRepo_existingBranch(t *testing.T) {
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
switch strings.Join(cmd.Args, " ") {
case "git config branch.feature.merge":
return &outputStub{[]byte("refs/heads/feature\n")}
return &test.OutputStub{[]byte("refs/heads/feature\n")}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -399,10 +400,10 @@ func TestPRCheckout_differentRepo_currentBranch(t *testing.T) {
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
switch strings.Join(cmd.Args, " ") {
case "git config branch.feature.merge":
return &outputStub{[]byte("refs/heads/feature\n")}
return &test.OutputStub{[]byte("refs/heads/feature\n")}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -452,7 +453,7 @@ func TestPRCheckout_maintainerCanModify(t *testing.T) {
return &errorStub{"exit status 1"}
default:
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()

View file

@ -3,45 +3,14 @@ package command
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"testing"
"github.com/cli/cli/context"
"github.com/cli/cli/git"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
)
func TestPrCreateHelperProcess(*testing.T) {
if test.SkipTestHelperProcess() {
return
}
args := test.GetTestHelperProcessArgs()
switch args[1] {
case "log":
fmt.Println("123,cool,\"\"")
case "status":
switch args[0] {
case "clean":
case "dirty":
fmt.Println(" M git/git.go")
default:
fmt.Fprintf(os.Stderr, "unknown scenario: %q", args[0])
os.Exit(1)
}
case "push":
default:
fmt.Fprintf(os.Stderr, "unknown command: %q", args[1])
os.Exit(1)
}
os.Exit(0)
}
func TestPRCreate(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
http := initFakeHTTP()
@ -52,11 +21,13 @@ func TestPRCreate(t *testing.T) {
} } } }
`))
origGitCommand := git.GitCommand
git.GitCommand = test.StubExecCommand("TestPrCreateHelperProcess", "clean")
defer func() {
git.GitCommand = origGitCommand
}()
cs := CmdStubber{}
teardown := utils.SetPrepareCmd(createStubbedPrepareCmd(&cs))
defer teardown()
cs.Stub("") // git status
cs.Stub("1234567890,commit 0\n2345678901,commit 1") // git log
cs.Stub("") // git push
output, err := RunCommand(prCreateCmd, `pr create -t "my title" -b "my body"`)
eq(t, err, nil)
@ -89,12 +60,14 @@ func TestPRCreate_web(t *testing.T) {
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
ranCommands := [][]string{}
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
ranCommands = append(ranCommands, cmd.Args)
return &outputStub{}
})
defer restoreCmd()
cs := CmdStubber{}
teardown := utils.SetPrepareCmd(createStubbedPrepareCmd(&cs))
defer teardown()
cs.Stub("") // git status
cs.Stub("1234567890,commit 0\n2345678901,commit 1") // git log
cs.Stub("") // git push
cs.Stub("") // browser
output, err := RunCommand(prCreateCmd, `pr create --web`)
eq(t, err, nil)
@ -102,9 +75,10 @@ func TestPRCreate_web(t *testing.T) {
eq(t, output.String(), "")
eq(t, output.Stderr(), "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n")
eq(t, len(ranCommands), 4)
eq(t, strings.Join(ranCommands[1], " "), "git push --set-upstream origin HEAD:feature")
eq(t, ranCommands[3][len(ranCommands[3])-1], "https://github.com/OWNER/REPO/compare/master...feature?expand=1")
eq(t, len(cs.Calls), 4)
eq(t, strings.Join(cs.Calls[2].Args, " "), "git push --set-upstream origin HEAD:feature")
browserCall := cs.Calls[3].Args
eq(t, browserCall[len(browserCall)-1], "https://github.com/OWNER/REPO/compare/master...feature?expand=1")
}
func TestPRCreate_ReportsUncommittedChanges(t *testing.T) {
@ -118,11 +92,13 @@ func TestPRCreate_ReportsUncommittedChanges(t *testing.T) {
} } } }
`))
origGitCommand := git.GitCommand
git.GitCommand = test.StubExecCommand("TestPrCreateHelperProcess", "dirty")
defer func() {
git.GitCommand = origGitCommand
}()
cs := CmdStubber{}
teardown := utils.SetPrepareCmd(createStubbedPrepareCmd(&cs))
defer teardown()
cs.Stub(" M git/git.go") // git status
cs.Stub("1234567890,commit 0\n2345678901,commit 1") // git log
cs.Stub("") // git push
output, err := RunCommand(prCreateCmd, `pr create -t "my title" -b "my body"`)
eq(t, err, nil)
@ -183,11 +159,13 @@ func TestPRCreate_cross_repo_same_branch(t *testing.T) {
} } } }
`))
origGitCommand := git.GitCommand
git.GitCommand = test.StubExecCommand("TestPrCreateHelperProcess", "clean")
defer func() {
git.GitCommand = origGitCommand
}()
cs := CmdStubber{}
teardown := utils.SetPrepareCmd(createStubbedPrepareCmd(&cs))
defer teardown()
cs.Stub("") // git status
cs.Stub("1234567890,commit 0\n2345678901,commit 1") // git log
cs.Stub("") // git push
output, err := RunCommand(prCreateCmd, `pr create -t "cross repo" -b "same branch"`)
eq(t, err, nil)

View file

@ -11,6 +11,7 @@ import (
"strings"
"testing"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
"github.com/google/shlex"
"github.com/spf13/cobra"
@ -110,7 +111,7 @@ func TestPRStatus_fork(t *testing.T) {
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
switch strings.Join(cmd.Args, " ") {
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
return &outputStub{[]byte(`branch.blueberries.remote origin
return &test.OutputStub{[]byte(`branch.blueberries.remote origin
branch.blueberries.merge refs/heads/blueberries`)}
default:
panic("not implemented")
@ -421,7 +422,7 @@ func TestPRView_previewCurrentBranch(t *testing.T) {
http.StubResponse(200, jsonFile)
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -456,7 +457,7 @@ func TestPRView_previewCurrentBranchWithEmptyBody(t *testing.T) {
http.StubResponse(200, jsonFile)
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -493,10 +494,10 @@ func TestPRView_currentBranch(t *testing.T) {
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
switch strings.Join(cmd.Args, " ") {
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
return &outputStub{}
return &test.OutputStub{}
default:
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -531,10 +532,10 @@ func TestPRView_noResultsForBranch(t *testing.T) {
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
switch strings.Join(cmd.Args, " ") {
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
return &outputStub{}
return &test.OutputStub{}
default:
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
}
})
defer restoreCmd()
@ -563,7 +564,7 @@ func TestPRView_numberArg(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -595,7 +596,7 @@ func TestPRView_numberArgWithHash(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -627,7 +628,7 @@ func TestPRView_urlArg(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -661,7 +662,7 @@ func TestPRView_branchArg(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -696,7 +697,7 @@ func TestPRView_branchWithOwnerArg(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()

View file

@ -11,6 +11,7 @@ import (
"time"
"github.com/cli/cli/context"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
)
@ -117,7 +118,7 @@ func TestRepoFork_in_parent_yes(t *testing.T) {
var seenCmds []*exec.Cmd
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmds = append(seenCmds, cmd)
return &outputStub{}
return &test.OutputStub{}
})()
output, err := RunCommand(repoForkCmd, "repo fork --remote")
@ -156,7 +157,7 @@ func TestRepoFork_outside_yes(t *testing.T) {
var seenCmd *exec.Cmd
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})()
output, err := RunCommand(repoForkCmd, "repo fork --clone OWNER/REPO")
@ -188,7 +189,7 @@ func TestRepoFork_outside_survey_yes(t *testing.T) {
var seenCmd *exec.Cmd
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})()
oldConfirm := Confirm
@ -227,7 +228,7 @@ func TestRepoFork_outside_survey_no(t *testing.T) {
cmdRun := false
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
cmdRun = true
return &outputStub{}
return &test.OutputStub{}
})()
oldConfirm := Confirm
@ -263,7 +264,7 @@ func TestRepoFork_in_parent_survey_yes(t *testing.T) {
var seenCmds []*exec.Cmd
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmds = append(seenCmds, cmd)
return &outputStub{}
return &test.OutputStub{}
})()
oldConfirm := Confirm
@ -311,7 +312,7 @@ func TestRepoFork_in_parent_survey_no(t *testing.T) {
cmdRun := false
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
cmdRun = true
return &outputStub{}
return &test.OutputStub{}
})()
oldConfirm := Confirm
@ -369,7 +370,7 @@ func TestRepoClone(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -409,7 +410,7 @@ func TestRepoCreate(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -474,7 +475,7 @@ func TestRepoCreate_org(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -539,7 +540,7 @@ func TestRepoCreate_orgWithTeam(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -591,7 +592,7 @@ func TestRepoView(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -624,7 +625,7 @@ func TestRepoView_ownerRepo(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()
@ -656,7 +657,7 @@ func TestRepoView_fullURL(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
return &test.OutputStub{}
})
defer restoreCmd()

View file

@ -2,11 +2,42 @@ package command
import (
"errors"
"fmt"
"os/exec"
"github.com/cli/cli/api"
"github.com/cli/cli/context"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
)
// TODO this is split between here and test/helpers.go. I did that because otherwise our test
// package would have to import utils (for utils.Runnable) which felt wrong. while utils_test
// currently doesn't import test helpers, i don't see why that ought to be precluded.
// I'm wondering if this is a case for having a global package just for storing Interfaces.
type CmdStubber struct {
Stubs []*test.OutputStub
Count int
Calls []*exec.Cmd
}
func (cs *CmdStubber) Stub(desiredOutput string) {
// TODO maybe have some kind of command mapping but going simple for now
cs.Stubs = append(cs.Stubs, &test.OutputStub{[]byte(desiredOutput)})
}
func createStubbedPrepareCmd(cs *CmdStubber) func(*exec.Cmd) utils.Runnable {
return func(cmd *exec.Cmd) utils.Runnable {
cs.Calls = append(cs.Calls, cmd)
call := cs.Count
cs.Count += 1
if call >= len(cs.Stubs) {
panic(fmt.Sprintf("more execs than stubs. most recent call: %v", cmd))
}
return cs.Stubs[call]
}
}
func initBlankContext(repo, branch string) {
initContext = func() context.Context {
ctx := context.NewBlank()
@ -27,19 +58,6 @@ func initFakeHTTP() *api.FakeHTTP {
return http
}
// outputStub implements a simple utils.Runnable
type outputStub struct {
output []byte
}
func (s outputStub) Output() ([]byte, error) {
return s.output, nil
}
func (s outputStub) Run() error {
return nil
}
type errorStub struct {
message string
}

View file

@ -1,58 +1,38 @@
package git
import (
"fmt"
"os"
"regexp"
"os/exec"
"testing"
"github.com/cli/cli/test"
"github.com/cli/cli/utils"
)
func TestGitStatusHelperProcess(*testing.T) {
if test.SkipTestHelperProcess() {
return
}
args := test.GetTestHelperProcessArgs()
switch args[0] {
case "no changes":
case "one change":
fmt.Println(" M poem.txt")
case "untracked file":
fmt.Println(" M poem.txt")
fmt.Println("?? new.txt")
case "boom":
os.Exit(1)
}
os.Exit(0)
}
func Test_UncommittedChangeCount(t *testing.T) {
origGitCommand := GitCommand
defer func() {
GitCommand = origGitCommand
}()
cases := map[string]int{
"no changes": 0,
"one change": 1,
"untracked file": 2,
type c struct {
Label string
Expected int
Output string
}
cases := []c{
c{Label: "no changes", Expected: 0, Output: ""}, // TODO will this fail and be seen as one newline?
c{Label: "one change", Expected: 1, Output: " M poem.txt"},
c{Label: "untracked file", Expected: 2, Output: " M poem.txt\n?? new.txt"},
}
for k, v := range cases {
GitCommand = test.StubExecCommand("TestGitStatusHelperProcess", k)
teardown := utils.SetPrepareCmd(func(*exec.Cmd) utils.Runnable {
return &test.OutputStub{}
})
defer teardown()
for _, v := range cases {
_ = utils.SetPrepareCmd(func(*exec.Cmd) utils.Runnable {
return &test.OutputStub{[]byte(v.Output)}
})
ucc, _ := UncommittedChangeCount()
if ucc != v {
t.Errorf("got unexpected ucc value: %d for case %s", ucc, k)
if ucc != v.Expected {
t.Errorf("got unexpected ucc value: %d for case %s", ucc, v.Label)
}
}
GitCommand = test.StubExecCommand("TestGitStatusHelperProcess", "boom")
_, err := UncommittedChangeCount()
errorRE := regexp.MustCompile(`git\.test(\.exe)?: exit status 1$`)
if !errorRE.MatchString(err.Error()) {
t.Errorf("got unexpected error message: %s", err)
}
}

View file

@ -8,36 +8,17 @@ import (
"path/filepath"
)
func GetTestHelperProcessArgs() []string {
args := os.Args
for len(args) > 0 {
if args[0] == "--" {
args = args[1:]
break
}
args = args[1:]
}
return args
// OutputStub implements a simple utils.Runnable
type OutputStub struct {
Out []byte
}
func SkipTestHelperProcess() bool {
return os.Getenv("GO_WANT_HELPER_PROCESS") != "1"
func (s OutputStub) Output() ([]byte, error) {
return s.Out, nil
}
func StubExecCommand(testHelper string, desiredOutput string) func(...string) *exec.Cmd {
return func(args ...string) *exec.Cmd {
cs := []string{
fmt.Sprintf("-test.run=%s", testHelper),
"--", desiredOutput}
cs = append(cs, args...)
env := []string{
"GO_WANT_HELPER_PROCESS=1",
}
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = append(env, os.Environ()...)
return cmd
}
func (s OutputStub) Run() error {
return nil
}
type TempGitRepo struct {